1

右辺値参照を使用して gcc で正常に動作する引数を格納するコールバック実装がありますが、一部のコードでは VS 2010 でコンパイルできません。短いバージョン:

#include <iostream>
#include <string>

class B {
public:
    virtual void execute() = 0;
};

template<typename FuncType, typename ArgType>
class F : public B {
public:
    F(FuncType func, ArgType && arg) : f(func), arg(arg) {}
    void execute() { f(arg); }
private:
    FuncType f;
    ArgType && arg;
};

template<typename FuncType, typename ArgType>
B * registerFunc(FuncType func, ArgType && arg)
{
    return new F<FuncType, ArgType>(func, arg);
}

void myFunction(std::string text)
{
    std::cout << "Function " << text << " here" << std::endl;
}

int main()
{
    const char text1[] = "sample1";
    std::string text2("sample2");

    B * b = registerFunc(myFunction, text1);
    b->execute();
    delete b;

    b = registerFunc(myFunction, text2);
    b->execute();
    delete b;

    // VS 2010 fails because of this call
    b = registerFunc(myFunction, text2.c_str());
    b->execute();
    delete b;

    return 0;
}

gcc 4.4 では、以下が生成されます。

$ g++ clbck.cpp -std=c++0x -o clbck && ./clbck
関数 sample1 はこちら
関数 sample2 はこちら
関数 sample2 はこちら

ただし、マークされた行のために registerFunc をインスタンス化しようとすると、VS 2010 でコンパイルに失敗します。

エラー C2664: 'F::F(FuncType,ArgType &&)' : パラメータ 2 を 'const char *' から 'const char *&&' に変換できません
[
FuncType
=void (__cdecl *)(std::string),
ArgType =const char *
]
左辺値を右辺値参照にバインドすることはできません

Googling は VS2010 の Boost 1.44 で同様のエラーを発見しましたが、推奨される解決策は右辺値参照をまったく使用しないことです。本当に他に方法はないのでしょうか?

そして、あなたがそれに取り組んでいる間、これらのコールバックを処理している方法に何か問題がありますか? 関数ポインターとファンクター (私はまだラムダをテストしていません) で問題なく動作します。私が見つけた唯一の欠点は、上記のものです。(ここに示されているコードは単なるデモであり、実際のコードではユーザーにポインターを提供していないことに注意してください。実際にこれを使用して、Qt アプリケーションのさまざまなスレッドで関数を実行しています)。

4

3 に答える 3

2

Visual Studio が不平を言うのは正しいと思います。ここで説明を見つけることができます。

ではregisterFunc、エクスプレッションarglvalue(名前を持っています) です。右辺値参照として転送する場合は、次を使用する必要がありますstd::forward

template<typename FuncType, typename ArgType>
B * registerFunc(FuncType func, ArgType && arg)
{
    return new F<FuncType, ArgType>(func, std::forward<ArgType>(arg));
}

コンストラクターでも同じ問題が発生しますが、そこにFuncType追加するstd::forwardと興味深い警告が表示されます。

参照メンバーは、コンストラクターの終了後に保持されない一時的に初期化されます

残念ながら、さらに役立つ右辺値参照が十分ではありませんが、右辺値参照メンバーが意味をなすかどうかは疑問です: 右辺値参照は、死にかけているオブジェクトにバインドされます。

于 2010-12-18T17:46:15.770 に答える
1

右辺値参照メンバーに関して何を達成したいのかは明確ではありません。正しく見えません。参照の保存は避けてください。関数オブジェクトに参照のようなオブジェクトを格納する場合は、std::refやstd::crefなどを引き続き使用できます(std :: bindの動作とよく似ています)。

遭遇した問題は、ターゲットタイプの左辺値で右辺値参照を初期化することが許可されていないことです。ただし、コンストラクターのパラメーター "arg"は名前付き右辺値参照であり、初期化子リストの左辺値式になるため、これを実行しようとします。このコードをコンパイルするために、おそらく古いGCCバージョンを使用しました。新しいバージョンもこれについて不平を言うでしょう。

std :: bind:に依存することで、トラブルを回避できます。

template<class FuncType>
class F : public B {
public:
  explicit F(FuncType func)
  : f(std::forward<FuncType>(func))
  {}
  void execute() { f(); }
private:
  FuncType f;
};

....

auto fun = std::bind(myFunction,text2.c_str());
B* ptr = new F<decltype(fun)>(fun);

本当に自分でパラメータバインディングを処理したい場合は、次のようにする必要があります。

template<class FuncType, class ParamType>
class F : public B {
public:
  F(FuncType func, ParamType para)
  : f(std::forward<FuncType>(func))
  , p(std::forward<ParamType>(para))
  void execute() { f(p); }
private:
  FuncType f;
  ParamType p;
};

Tを推定できるパラメータタイプT&&には特別な意味があることに注意してください。これは「キャッチオール」パラメータです。テンプレート引数の推論は、引数が左辺値であった場合、Tを左辺値参照(および参照折りたたみルールによるT &&)にします。したがって、パラメータを常にコピーとして保存する場合は、次のように記述する必要があります。

template<class FuncType, class ArgType>
B * registerFunc(FuncType func, ArgType && arg)
{
  typedef typename std::decay<ArgType>::type datype;
  return new F<FuncType,datype>(func, std::forward<ArgType>(arg));
}

私はこのようなregisterFunc関数を好みます。デフォルトでは、関数オブジェクトとパラメータオブジェクトをコピーします。これをオーバーライドするには、std::refおよびstd::cref:を使用します。

registerFunc(some_func,std::cref(some_obj));
于 2010-12-18T18:22:10.357 に答える
0

ラムダ式を使用しないのはなぜですか?

class B {
    virtual void execute() = 0;
};
template<typename T> class F : public B {
    T t;
public:
    F(T&& arg) : t(std::forward<T>(arg)) {}
    void execute() { return t(); }
};
template<typename T> F<T> make_f(T&& t) {
    return F<T>(std::forward<T>(t));
}
int main() {
    std::string var;
    B* callback = make_f([=]() {
        std::cout << var << std::endl;
    });
}

または、実際にはstd::function<void()>、それが目的です。

于 2010-12-19T11:34:18.727 に答える