5

以下のように、C++ 11でラムダを使用してfinallyシミュレーターを作成しました。

#include <cstdio>

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor& func) : func_(func) {} // (1)
    ~Finalizer() { func_(); }

private:
    Functor func_; // (2)
};

template<typename functor>
Finalizer<functor> finally(functor& func)
{
    return Finalizer<functor>(func); (3)
}

int main()
{
    int a = 20;

    // print the value of a at the escape of the scope
    auto finalizer = finally([&]{ printf("%d\n", a); }); // (4)
}

コードは意図したとおりに動作しますが、 Finalizer構造体 (1)の ctor で望ましくない (ラムダ ファンクターの) copy ctor 呼び出しがあります。(ありがたいことに、 finally関数 (3 -> 4)の return ステートメントでのコピー構築は、RVO によって回避されます。)

コンパイラはコピー ctor 呼び出しを排除しません (少なくとも vc10 では - gcc はそれを最適化する可能性があります)。Finalizer struct (2) のファンクターの型が参照に変更されると、 finally呼び出しでのラムダ引数( 4) は r 値です。

もちろん、コードは以下のように「最適化」できます

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor& func) : func_(func) {}
    ~Finalizer() { func_(); }

private:
    Functor& func_;
};

int main()
{
    int a = 20;

    auto finalizer = [&]{ printf("%d\n", a); };
    Finalizer<decltype(finalizer)> fin(finalizer);
}

オーバーヘッドはなく、printf呼び出しのみがスコープの最後に配置されます。でも… 好きじゃない。:(マクロでラップしようとしましたが、2つの「名前」を宣言する必要があります-1つはラムダオブジェクト用、もう1つはファイナライザーオブジェクト用です。

私の目的は単純です -

  1. 回避できる不要なパフォーマンスのオーバーヘッドはすべて排除する必要があります。理想的には、関数呼び出しがなく、すべてのプロシージャがインライン化されている必要があります。
  2. 効用関数の目的として簡潔な式を維持します。マクロの使用は許可されていますが、お勧めできません。

この状況でそれを回避する解決策はありますか?

4

3 に答える 3

4

ラムダには移動コンストラクターがあると思いますか? もしそうなら、そしてfinally内で右辺値のみを使用する場合、&&forwardはコピーではなく移動します。

#include <cstdio>

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor&& func) : func_(std::move(func)) {}
    Finalizer(Functor const& func) : func_(func) {} // (1)
    ~Finalizer() { func_(); }

private:
    Functor func_; // (2)
};

template<typename functor>
Finalizer<std::remove_reference<functor>::type> finally(functor&& func)
{
    return Finalizer<std::remove_reference<functor>::type>(std::forward<functor>(func)); // (3)
}

int main()
{
    int a = 20;

    // print the value of a at the escape of the scope
    auto finalizer = finally([&]{ printf("%d\n", a); }); // (4)
}

左辺値でも正しく機能するよりインテリジェントなものを修正できるはずです。これにより、「最適化された」バージョンがコンパイルされ、移動できないときにコピーされます。その場合、パラメータが渡されたかFunctor<std::remove_reference<functor>::type>どうかに関係なく、 Functor が正しいタイプであることを確認するようなものを使用することをお勧めします。&&&

于 2012-01-18T01:20:05.673 に答える
0

おそらく、コンストラクターへのファンクター引数を右辺値参照として受け入れますか?

于 2012-01-18T01:12:01.157 に答える