7

VC11 と g++ 4.7.2 を使用して、次の例をコンパイルしようとしました。

#include <functional>

class X {
public:
  template <typename T>
  explicit X(T t)
  {
    std::bind(&X::invoke<T>, this, t)();
  }
private:
  template <typename T>
  void invoke(T t)
  {
    t();
  }
};

class Y {
  public:
    void foo() {
      //...
    }
};


int main() {
  Y y;
  X x(std::bind(&Y::foo, &y));
  return 0;
}

しかし、それはエラーで終了しました。コンパイラの出力全体を貼り付けることが合理的かどうかはわかりませんが、一般的には

vc11 言います:

エラー C2664: 'void std::_Pmf_wrap::operator ()(_Farg0 &,_V0_t) const': パラメーター 3 を 'void' から 'std::_Bind,Y *,std::_Nil,std::_Nil に変換できません,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' c:\program files (x86)\microsoft visual studio 11.0\vc\include\functional 1152 1 ConsoleApplication1 (Microsoft Visual C++ コンパイラ Nov 2012 CTP)

および g++:

コンパイルはエラーで終了しました:
source.cpp: 'X::X(T) [with T = std::_Bind(Y*)>]' のインスタンス化中:
source.cpp:28:33: ここから必要
source.cpp :8:9: エラー: '(std::_Bind_helper(Y*)>)、X* const、std::_Bind(Y*)>&>::type {別名 std::_Bind( Y*)>)>(X*, std::_Bind(Y*)>)>}) ()'

この問題を解決する方法はありますか。私にとって非常に重要なことは、主要なアイデアを保存することです。つまり、任意の呼び出し可能なオブジェクト (関数オブジェクト、関数ポインター、または関数によって返される呼び出しラッパーstd::bind()) でインスタンス化できるクラスです。

誰かが助けてくれたら幸いです。

PS のインスタンスを作成し、X関数オブジェクトまたは関数ポインタを渡すとコンパイルされます。

4

2 に答える 2

4

boost::bindに採用する際にの重要な部分std::bind、つまりを省略したと思いますboost::protect()。コードは次のように修正できます。

#include <boost/bind/protect.hpp>
// ...
X x(boost::protect(std::bind(&Y::foo, &y)));

または、代わりに:

template <typename T>
explicit X(T t)
{
    auto tt = boost::protect(t);
    auto f = std::bind(&X::invoke<decltype(tt)>, this, tt);
    f();
}

http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.htmlを参照してください。

デフォルトでは、最初の引数は評価されませんが、他のすべての引数は評価されます。ネストされたバインド部分式であっても、最初の引数に続く引数を評価しないことが必要な場合があります。これは、バインドが型を認識および評価しないように型をマスクする、別の関数オブジェクト、protect の助けを借りて実現できます。呼び出されると、protect は単に引数リストを変更せずに他の関数オブジェクトに転送します。

ヘッダー boost/bind/protect.hpp には、保護の実装が含まれています。バインド関数オブジェクトを評価から保護するには、protect(bind(f, ...)) を使用します。


Scott Meyers による今後の『Effective C++11: Content and Status 』では、ラムダを std::bind よりも優先することを推奨する予定です。C++11 では、次のように簡単に実行できます。

template <typename T>
explicit X(T t)
{
    auto f = [t, this]() { this->invoke(t); };
    f();
}
// ...

X x([&y](){ y.foo(); });
于 2013-02-27T15:30:11.473 に答える
2

std::bindこの問題の根本的な原因は、特に を参照して、によって実行される引数の内部コピーにあるようtです。

次の方法で回避できます。

  template <typename T>
  explicit X(T t)
  {
      std::bind(&X::invoke<T>, this, std::placeholders::_1)(t);
      //                             ^^^^^^^^^^^^^^^^^^^^^  ^
  }

これも機能しますが、 の結果をbind引数より長く有効にすることはできませんt(そうしないと、 へのダングリング参照を渡すことになりますinvoke<T>()):

  template <typename T>
  explicit X(T t)
  {
      std::bind(&X::invoke<T>, this, cref(t))();
      //                             ^^^^^^^
  }

アップデート:

上記の解決策は、例で示していることを達成するのに役立つ回避策です。ただし、ユースケースがまったく異なる可能性があるというコメントから明らかになりました(たとえばbind、後で評価するために結果を渡すなど)。

Maxim Yegorushkinが彼の回答で正しく指摘したように、概念的に正しい解決策は、Boost のような構造を使用することprotectです。

Boost を使用したくない場合は、protect()C++11 の可変個引数テンプレートを使用して独自の関数を簡単に定義できます。

// Imitates boost::protect, but with variadic templates and perfect forwarding
namespace detail
{
    template<typename F>
    struct protect
    {
    private:

        F _f;

    public:

        explicit protect(F f): _f(f)
        {
        }

        template<typename... Ts>
        auto operator () (Ts&&... args) -> 
            decltype(_f(std::forward<Ts>(args)...))
        {
            return _f(std::forward<Ts>(args)...);
        }
    };
}

template<typename F>
detail::protect<F> protect(F&& f)
{
    return detail::protect<F>(std::forward<F>(f));
}

マキシムが提案したように、最終的には、これがクラスで使用できる方法です。

class X
{
public:
    template <typename T>
    explicit X(T t)
    {
        auto pt = protect(t);
        std::bind(&X::invoke<decltype(pt)>, this, pt)();
    }
private:
    template <typename T>
    void invoke(T t)
    {
        t();
    }
};
于 2013-02-27T15:14:13.193 に答える