2017-07-31 6 views
1

は、次のコードを考えてみましょう:私はそのテンプレート型引数を指定せずにLambdaSubtaskオブジェクトを宣言することはできません、それはラムダですので、私はそのテンプレート型引数を指定することはできませんので、私はファクトリメソッドを実装しようラムダでテンプレート化されたオブジェクトを返す方法は?

class BaseTask { 
    // Implementation here 
}; 

class BaseSubtask { 
    BaseTask *_pTask; 
public: 
    explicit BaseSubtask(BaseTask *pTask) : _pTask(pTask) { } 
    virtual void Run() = 0; 
    // The rest of implementation here. 
}; 

template<typename taFunc> class LambdaSubtask { 
    taFunc _f; 
public: 
    explicit LambdaSubtask(BaseTask *pTask, taFunc&& f) 
    : BaseSubtask(pTask), _f(std::forward<taFunc>(f)) 
    { } 
    LambdaSubtask(const LambdaSubtask&) = delete; 
    LambdaSubtask& operator=(const LambdaSubtask&) = delete; 
    LambdaSubtask(LambdaSubtask&&) = delete; 
    LambdaSubtask& operator=(LambdaSubtask&&) = delete; 
    virtual void Run() override final { _f(); } 
    // The rest of implementation here 
}; 

を:

適切で

copy-list-initialization of LambdaSubtask<lambda_...> cannot use an explicit constructor

:残念ながら、これはコンパイルエラーを与える

template<typename taFunc> inline LambdaSubtask<taFunc> 
MakeLambdaSubtask(BaseTask *pTask, taFunc&& f) { 
    return { pTask, std::forward<taFunc>(f) }; 
} 

次のようにファクトリメソッドは、私がLambdaSubtaskオブジェクトを取得できます。

BaseTask task; // Initialization of the task is skipped in the example 
auto&& lst = MakeLambdaSubtask(&task, [/* Capture here*/]() { 
    // Implementation here 
}); 

だから、基本的に私は、テンプレートの種類は、ラムダことで、LambdaSubtask型のローカル変数オブジェクトをしたいです。私は何かを余分にコピーしないようにしたい。確かに私はベンチマークが非常に遅いことを示すようにstd::functionを避けたいと思う。

適切なファクトリメソッドを実装する方法、またはローカル変数オブジェクトLambdaSubtaskを別の方法で取得する方法を知っていますか?

コンパイラはMSVC++ 2017でツールセットv141を使用しているため、C++ 11/14/17は部分的にサポートされています。

+0

'{のstd前方:: pTask (F)} LambdaSubtask を返す;'、あなたはおそらくLambdaSubtask <はstd :: decay_t > ' – Justin

+0

@Justinは、私の知る限り、これはコピー/移動コンストラクタを伴うだろう'たいが、 'LambdaSubtask'。'LambdaSubtask'自体の移動は効率化することができますが、メンバー変数として持つラムダの効率がどれだけ効率的かはわかりません。 –

+0

関数型 'F'を持っている場合、rvalueまたはlvalueを使って' MakeLambdaSubtask'を呼び出したかどうかによって 'taFunc'は' F'または何らかの 'F&'になります。もしそれが 'F&'だったならば、あなたの 'LambdaSubtask'は動かない、割り当て不可能な、などです。それはあなたが望むものかもしれませんが、予期しないことです。私は予期しない動作を避ける傾向がありますが、それはあなた次第です – Justin

答えて

2

基本的に、あなたはこれを行うにしようとしています。回避策は、明示的に次のように構成することです:

X foo() { return X{4}; } 
//    ^^^ 

注意:C++ 17では、これはコピーまたは移動を行わないことに注意してください。 C++ 14より前のバージョンでは、コピーや移動のいずれも発生しませんでしたが、移動はまだ整形式でなければなりません。

もう1つの方法は、コンストラクタからexplicitマークを削除することです。これは、braced-init-listを返さないようにするものです。サイドノートとして


、にご注意:あなたが左辺値の関数に渡されている場合は

template<typename taFunc> 
LambdaSubtask<taFunc> MakeLambdaSubtask(BaseTask *pTask, taFunc&& f) { ... } 

、あなたはそれへの参照を保持しようとしている - そのためには、余分な寿命ですあなたは心配する必要があります。このため、代わりにLambdaSubtask<std::decay_t<taFunc>>を返すのが一般的です。これにより、サブタスクが有効期間内に有効な機能を持つことが保証されます。

+0

いいえ、重要な制限がありません:私は余分なコピー/移動コンストラクタ呼び出しを避けようとしています。 AFAIK、あなたの例の関数はその制限に違反します。 –

+0

@SergeRogatch C++ 17では、この例ではコピー/移動はありません。 – Barry

+0

MSVC++ 2017ツールセットv141(最新)は、このC++の部分をサポートしていないようです... –

3

ラムダコンストラクタを明示的に宣言することで、自分自身で行いました。明示的に削除すると、コードがコンパイルされます。

統一リストの初期化は、明示的なコンストラクタでは機能しません。 Xコンストラクタがexplicitであり、あなたがコピーリストの初期化をやっているので、動作しません

struct X { 
    explicit X(int) { } 
}; 

X foo() { return {4}; } 

+0

どのような素晴らしいヒット!驚くべきことは単純です...実際、コードは '明示的に'を取り除いた後にコンパイルされます。 –

関連する問題