75

私のC++アプリケーション(Visual Studio 2010を使用)では、次のようにstd::functionを格納する必要があります。

class MyClass
   {
   public:
      typedef std::function<int(int)> MyFunction;
      MyClass (Myfunction &myFunction);
   private:
      MyFunction m_myFunction;    // Should I use this one?
      MyFunction &m_myFunction;   // Or should I use this one?
   };

ご覧のとおり、コンストラクターの参照として関数の引数を追加しました。

しかし、私のクラスに関数を格納するための最良の方法は何ですか?

  • std :: functionは単なる関数ポインタであり、関数の「実行可能コード」はメモリに残ることが保証されているので、関数を参照として保存できますか?
  • ラムダが渡されて呼び出し元が戻ってきた場合に備えて、コピーを作成する必要がありますか?

私の直感では、参照(const-referenceでも)を保存しても安全だと言っています。コンパイラがコンパイル時にラムダのコードを生成し、アプリケーションの実行中はこの実行可能コードを「仮想」メモリに保持することを期待しています。したがって、実行可能コードが「削除」されることはなく、その参照を安全に保存できます。しかし、これは本当に本当ですか?

4

5 に答える 5

74

std :: functionは単なる関数ポインタであり、関数の「実行可能コード」はメモリに残ることが保証されているので、関数を参照として保存できますか?

std::function単なる関数ポインタではありません。これは、任意の呼び出し可能オブジェクトのラッパーであり、そのオブジェクトを格納するために使用されるメモリを管理します。他のタイプと同様に、参照が使用されるときはいつでも参照されるオブジェクトが引き続き有効であることを保証する他の方法がある場合にのみ、参照を保存するのが安全です。

参照を保存する正当な理由がなく、参照が有効であり続けることを保証する方法がない限り、値で保存してください。

コンストラクターへの参照による受け渡しconstは安全であり、おそらく値を渡すよりも効率的です。非const参照で渡すことは、一時的なものを渡すことができないため、悪い考えです。そのため、ユーザーは、ラムダ、結果bind、またはstd::function<int(int)>それ自体を除く他の呼び出し可能なオブジェクトを直接渡すことはできません。

于 2012-01-03T11:19:38.903 に答える
20

関数を参照によってコンストラクターに渡し、そのコピーを作成しない場合、参照が無効になるため、関数がこのオブジェクトの範囲外になると運が悪くなります。それは以前の回答ですでに言われています。

私が追加したかったのは、代わりに、参照ではなくで関数をコンストラクターに渡すことができるということでした。なんで?とにかくそのコピーが必要なので、値を渡すと、コンパイラは一時が渡されたときにコピーを作成する必要性を最適化できます(インプレースで記述されたラムダ式など)。

もちろん、どのように実行しても、渡された関数を変数に割り当てるときに別のコピーを作成する可能性があるため、を使用std::moveしてそのコピーを削除します。例:

class MyClass
{
public:
   typedef std::function<int(int)> MyFunction;

   MyClass (Myfunction myFunction): m_myfunction(std::move(myFunction))
       {}

private:
   MyFunction m_myFunction;
};

したがって、それらが上記に右辺値を渡す場合、コンパイラーは最初のコピーをコンストラクターに最適化し、std::moveは2番目のコピーを削除します:)

(唯一の)コンストラクターがconst参照を取得する場合は、渡される方法に関係なく、関数内でそのコピーを作成する必要があります

別の方法は、左辺値と右辺値を別々に処理するために、2つのコンストラクターを定義することです。

class MyClass
{
public:
   typedef std::function<int(int)> MyFunction;

   //takes lvalue and copy constructs to local var:
   MyClass (const Myfunction & myFunction): m_myfunction(myFunction)
       {}
   //takes rvalue and move constructs local var:
   MyClass (MyFunction && myFunction): m_myFunction(std::move(myFunction))
       {}

private:
   MyFunction m_myFunction;
};

これで、別の方法で右辺値を手動で再評価し、その場合は明示的に処理することでコピーする必要がなくなります(コンパイラーに処理させるのではありません)。最初のものよりわずかに効率的かもしれませんが、より多くのコードでもあります。

(おそらくこのあたりでかなり見られます)関連するリファレンス(そして非常に良い読み物): http ://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

于 2013-04-19T11:53:04.243 に答える
3

好きなだけコピーしてください。コピー可能です。標準ライブラリのほとんどのアルゴリズムでは、ファンクターが必要です。

ただし、重要なケースでは参照による受け渡しの方がおそらく高速であるため、ライフサイクル管理について気にする必要がないように、一定の参照による受け渡しと値による保存をお勧めします。それで:

class MyClass
{
public:
    typedef std::function<int(int)> MyFunction;
    MyClass (const Myfunction &myFunction);
          // ^^^^^ pass by CONSTANT reference.
private:
    MyFunction m_myFunction;    // Always store by value
};

定数または右辺値の参照を渡すことにより、関数を呼び出すことができる間は関数を変更しないことを呼び出し元に約束します。これにより、関数を誤って変更することを防ぎます。戻り値を使用するよりも読みにくいため、通常は意図的に変更することは避けてください。

編集:私はもともと上記の「定数または右辺値」と言いましたが、デイブのコメントは私にそれを調べさせました、そして実際に右辺値参照は左辺値を受け入れません。

于 2012-01-03T11:19:00.157 に答える
3

コピーを作成することをお勧めします。

MyFunction m_myFunction; //prefferd and safe!

元のオブジェクトがスコープ外に出てそれ自体を破壊した場合でも、コピーはクラスインスタンスに存在するため、安全です。

于 2012-01-03T11:20:27.787 に答える
3

原則として(特に、高度にスレッド化されたシステムでこれらを使用している場合)、値を渡します。スレッド内から、基になるオブジェクトがまだ参照型で存在していることを確認する方法はありません。そのため、非常に厄介なレースやデッドロックのバグに直面することになります。

もう1つの考慮事項は、std :: function内の非表示の状態変数です。この変数の変更は、スレッドセーフである可能性はほとんどありません。これは、基になる関数呼び出しがスレッドセーフであっても、その周りのstd :: functionラッパーの「()」呼び出しはそうではない可能性があることを意味します。std :: functionのスレッドローカルコピーを常に使用することで、目的の動作を回復できます。これは、それぞれに状態変数の分離されたコピーがあるためです。

于 2013-08-26T18:24:34.637 に答える