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
}

コメントしたようにラムダを使用するより一般的なソリューションの場合、次のようになります。

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( );

以下が有効であるため、funcがvoidまたはtypeを返す場合の両方。

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 に答える