私は次を試しました:
std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
//The caller given ownership of psomething
return [psomething](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
しかし、コンパイルはしません。何か案は?
更新:
提案されているように、所有権をラムダに転送する必要があることを明示的に指定するには、いくつかの新しい構文が必要です。現在、次の構文について考えています。
std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
//The caller given ownership of psomething
return [auto psomething=move(psomething)](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
それは良い候補でしょうか?
更新1:
move
とcopy
の実装を次のように示します。
template<typename T>
T copy(const T &t) {
return t;
}
//process lvalue references
template<typename T>
T move(T &t) {
return std::move(t);
}
class A{/*...*/};
void test(A &&a);
int main(int, char **){
A a;
test(copy(a)); //OK, copied
test(move(a)); //OK, moved
test(A()); //OK, temporary object
test(copy(A())); //OK, copying temporary object
//You can disable this behavior by letting copy accepts T &
//test(move(A())); You should never move a temporary object
//It is not good to have a rvalue version of move.
//test(a); forbidden, you have to say weather you want to copy or move
//from a lvalue reference.
}
この問題は、C++ 14の lambda generalized capture で対処されています。
// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters);
// move the unique_ptr into the lambda
go.run([u = move(u)]{do_something_with(u);});
ラムダでunique_ptr
を永続的にキャプチャすることはできません。実際、ラムダ内の何かを永続的にキャプチャする場合は、copyable;でなければなりません。単に移動するだけでは不十分です。
これはC++ 11の欠陥と見なされる可能性がありますが、unique_ptr
値をラムダに移動することを明示的に示すための構文が必要になります。 C++ 11仕様は、名前付き変数の暗黙的な移動を防ぐために非常に慎重に表現されています。それがstd::move
が存在する理由であり、これはgoodのことです。
必要なことを行うには、std::bind
(セミコンボリュートされ、短いbinds
シーケンスが必要)を使用するか、通常の古いオブジェクトを返す必要があります。
また、実際に移動コンストラクターを記述している場合を除き、unique_ptr
を&&
で取得しないでください。値でそれを取るだけです。ユーザーが値で提供できる唯一の方法は、std::move
を使用することです。実際、コンストラクター/割り当て演算子の移動(または転送関数の実装)を記述している場合を除き、&&
を使用しないことをお勧めします。
Nicol Bolasが述べたstd::bind
を使用した「半複雑な」ソリューションは、結局それほど悪くはありません。
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}
私のために働いた次善の解決策は、unique_ptr
からshared_ptr
その後、shared_ptr
ラムダ。
std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
//The caller given ownership of psomething
std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
return [psomethingShared]()
{
psomethingShared->do_some_thing();
};
}
unique_ptr
をshared_ptr
内に固定するという、非常に危険な回避策を使用しました。これは、コードにunique_ptr
(APIの制限により)が必要だったため、実際にそれをshared_ptr
に変換できなかったためです(そうでなければ、unique_ptr
を取得できません)バック)。
この忌み嫌いを使用する理由は、テストコード用であり、テスト関数呼び出しにstd::bind
a unique_ptr
する必要があったということです。
// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));
std::function<void()> fnTest = std::bind([this, sh, input, output]() {
// Move unique_ptr back out of shared_ptr
auto unique = std::move(*sh.get());
// Make sure unique_ptr is still valid
assert(unique);
// Move unique_ptr over to final function while calling it
this->run_test(std::move(unique), input, output);
});
fnTest()
を呼び出すと、unique_ptr
を渡しながらrun_test()
を呼び出します。 fnTest()
を再度呼び出すと、unique_ptr
が最初の呼び出し中に既に移動/失われているため、アサーションエラーが発生します。