2016-12-02 3 views
1

以下のコードでは、Fのmove ctorが呼び出されるのはなぜですか?テンプレートコンストラクタ(std :: functionとmini case)によってコンストラクタトリガが予期せず移動する

std :: functionをラップしようとしたときに私はこの問題に遭遇し、内部ファンクタが構築中に2回移動したことに驚きました。

(G ++ 4.8.x-5.3.xを使用して)
#include <iostream> 
using namespace std; 

struct F { 
    F() { cout << "F()" << endl; } 
    F(const F &) { cout << "F(const F &)" << endl; } 
    F(F &&) { 
     cout << "F(F &&)" << endl; 
    } 

    void operator()() { cout << "F::()()" << endl;} 
}; 

struct I { 
#if 0 
    // F's move ctor not invoked 
    I(F &&) { } 
#else 
    // F's move ctor invoked, why? 
    template<typename FT, typename = void> 
    I(FT) { } // Simulate std::function ctor prototype 
#endif 
}; 

struct FF { 
    FF(F &&f) : _impl(std::move(f)) {} // [line1] Debugger show source here 
    I _impl; 
}; 

int main() { 
    FF ff = F(); 
    (void)ff; 
    cout << "----" << endl; 
    return 0; 
} 

GDBは[LINE1]によって呼び出され、予期しない移動CTORことを示しているが、私はそれを理解することができません。 誰も私にそれを説明できますか?

さらに、私の実際のケースでは(クラスIをstd :: functionに置き換えて)、内部ファンクタFを2回動かさずにラッパークラスFFを構築する方法はありますか?

+0

これは、 'std :: forward'と転送参照の場所です。 – Danh

答えて

1

Iコンストラクタのテンプレートバージョンは、rvalue参照ではなく値でパラメータを受け取ります。 by-valueパラメータは、あなたが不思議に思う移動コンストラクタの呼び出しによって構築されます。

代わりに '汎用参照'スタイルのコンストラクタが必要です。このコンストラクタは、そのパラメータを右辺値参照として宣言します。これは、std::functionと他の構成で使用される完全転送を可能にするイディオムです。

ので変更:

template<typename FT, typename = void> 
I(FT) { } // Simulate std::function ctor prototype 

へ:

template<typename FT, typename = void> 
I(FT&&) { } // Simulate std::function ctor prototype 

はやや手の込んだ:あなたが考えているかもしれない、単にあなたの呼び出しで右辺値参照型を持つ値を提供することにより、コンパイラが希望値FTを値面参照としてインスタンス化します(F&&)。しかし、この状況でテンプレート減算がどのように機能するかは異なりません。コンパイラは「崩壊」タイプ(この場合はF)のみを推論します。パラメータを参照にする場合は、パラメータとして参照型を明示的に指定する必要があります。

+0

expalantionに感謝します。しかし、std :: functionの問題は解決できませんか? std :: function ctorは関数の形になっています(_Functor __f) – user2771324

+0

「問題」の意味に依存します。あなたが参照する 'std :: function' ctorは値によってfunctorパラメータをとります。これは、' std :: function' ctorが呼び出されたときに 'F'(コピーまたは移動)_must_が実行されることを意味します。あなたのラッパーはこれの上に別のctor呼び出しを追加する必要はありません。あなたは正確に何を心配していますか? – Smeeheey

+0

1. "use = std :: function ;"とし、 "FF ff = F();"とします。 F(F &&)を2回トリガーします。 2. "std :: function sf = F();" F(F &&)を1回だけトリガーします。 1の場合には、g ++でctorを呼び出すことができないか、またはspecによってそのような種類の最適化が行われないことがわかりません。 – user2771324

関連する問題