12

我有一个模板函数,它调用另一个函数并存储它的返回值,然后在返回值之前做一些工作。我想将此扩展到处理T = void,并且想知道专业化是否是我唯一的选择。

template<typename T>
T Foo( T(*Func)() ) 
{
    // do something first (e.g. some setup)
    T result = Func();
    // do something after (e.g. some tear down)
    return result;
}

// Is this specialization the only option?
template<>
void Foo<void>( void(*Func)() ) 
{
    // do something first (e.g. some setup)
    Func();
    // do something after (e.g. some tear down)
    return;
}

void Bar() {}
int BarInt() { return 1; }

int main()
{
    Foo<int>(&BarInt);
    Foo<void>(&Bar);
}

或者可以修改常规版本Foo以处理void类型并且在这种情况下基本上什么都不做?我在想也许我的本地结果可以包装在一个可以处理的类型中void,但也可以将分配视为一个交易破坏者。

4

4 回答 4

5

鉴于您的操作不依赖于函数的结果,您可以在没有专门化的情况下进行操作。函数返回void返回类型的表达式是可以的void。所以这return部分不是麻烦的部分,但你需要想出一种方法来进行前后操作。构造函数和析构函数将帮助您:

struct do_something_helper
{
    do_something_helper()
    {
        // do something first (e.g. take a lock)
    }
    ~do_something_helper()
    {
        // do something after (e.g. release a lock)
    }
};

然后你可以这样写你的函数:

template<typename T>
T Foo( T(*Func)() ) 
{
    do_something_helper _dummy_helper; // constructor called here

    return Func();
    // destructor called here
}

对于您评论的使用 lambdas 的更通用的解决方案,它可能如下所示:

template< typename Pre, typename Post >
struct scope_guard
{
    scope_guard( Pre&& pre, Post&& post )
      : _post( std::forward< Post >( post ) )
    {
        pre();
    }
    ~scope_guard()
    {
        _post();
    }

    Post _post;
};

template< typename Pre, typename Post >
scope_guard< Pre, Post > make_scope_guard( Pre&& pre, Post&& post )
{
    return scope_guard< Pre, Post >( std::forward< Pre >( pre ), std::forward< Post >( post ) );
}

template<typename T>
T Foo( T(*Func)() ) 
{
    auto do_something_helper =
        make_scope_guard(
            [](){ /* do something first (e.g. take a lock) */ },
            [](){ /* do something after (e.g. release a lock) */ }
        );

    return Func();
}

使用类型擦除的版本std::function< void() >会更容易编写和使用,但效率会相当低。

于 2013-01-07T23:25:30.397 回答
2

不,您需要专业化,因为您无法存储 void(如 中T result = Func();)。

如果您永远不会使用该值,并且您可以将 Func 作为最后一件事调用,那么您实际上可以:

return Func( );

if func 返回 void 或类型,因为以下是合法的:

void f();
void g() {
    return f();
}

但是如果需要临时存储返回值,则需要特化。

但是,如果您要创建很多这些辅助函数 (Foo),并且您不想每次都将特定的 Foo 专门化为 void,则可以创建一个包装调用程序,它将执行调用,并且需要时,返回值,无论是 void 还是某种真实类型:

template< typename R >
class call_wrapper {
public:
    call_wrapper( std::function< R( void ) > f )
    : temp( std::move( f( ) ) )
    { }

    R&& return_and_destroy( ) {
        return std::move( temp );
    }

private:
    R temp;
};

template< >
class call_wrapper< void > {
public:
    call_wrapper( std::function< void(void) > f ) { f( ); }

    void return_and_destroy( ) { }
};

然后,您可以在您要编写的所有 Foo 中执行以下操作,而无需专门化,因为它已在上面的包装器中一劳永逸地处理:

template<typename T>
T Foo( T(*Func)() ) 
{
    // do something first (e.g. some setup)
    call_wrapper< T > cw( Func );
    // do something after (e.g. some tear down)
    return cw.return_and_destroy( );
}
于 2013-01-07T23:16:58.420 回答
2

如果您所做的只是获取和释放锁,那么您应该使用 RAII 而不是调用锁定/解锁函数。如果Func可以抛出,尤其如此,就好像它不会调用它之​​后的代码一样。

一旦你有一个 RAII 锁(或者如果你需要多个锁,则需要一段时间的 RAII 对象),你可以简单地执行以下操作void

template<typename T>
T Foo( T(*Func)() ) 
{
    lock my_lock;
    return Func();
}

template<typename T>
T Foo( T(*Func)() ) 
{
    struct raii_wrapper {
      raii_wrapper(T(*Func)()) : Func(Func) {
        // pre effects
      }
      ~raii_wrapper() {
        // post effects
      }
      T(*Func)();
    } actions(Func);
    return Func;
}
于 2013-01-07T23:22:49.580 回答
0

不确定这是否有效,但是:

template<>
void Foo<T>( T(*Func)() ) 
{
    // do something first (e.g. some setup)
    return tearDown(Func());
}

template<>
T tearDown<T>( T result) 
{
    // do something after
    return result;
}
于 2013-01-07T23:40:45.673 回答