2015-09-09 3 views
15

This answerは、C++でラムダ内の変数を移動キャプチャする方法を説明しています。ラムダの移動:移動可能なタイプを移動キャプチャした後、どのようにラムダを使用できますか?

しかし、ラムダ内のコピー不可能なオブジェクト(std::unique_ptrなど)を移動キャプチャした後は、ラムダ自体をコピーすることはできません。

あなたはラムダを移動しますが、そうしようとしたとき、私はコンパイルエラーを取得することができればこれは罰金のようになります。

using namespace std; 

class HasCallback 
{ 
    public: 
    void setCallback(std::function<void(void)>&& f) 
    { 
     callback = move(f); 
    } 

    std::function<void(void)> callback; 
}; 

int main() 
{ 
    auto uniq = make_unique<std::string>("Blah blah blah"); 
    HasCallback hc; 
    hc.setCallback(
     [uniq = move(uniq)](void) 
     { 
     std::cout << *uniq << std::endl; 
     }); 

    hc.callback(); 
} 

これはg++と、次のエラーを生成し(私がコピーしようとしてきました関連する行のみ):

error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&’ 

...ラムダを移動しようとする試みが失敗したと考えています。

clang++も同様のエラーが発生します。

私は(それは一時的な値だが)ラムダをINGの明示的moveを試してみましたが、それは助けにはなりませんでした。

EDIT:以下の回答は、上記のコードで生成されたコンパイルエラーを適切に解決します。別のアプローチの場合は、単にreleasestd::shared_ptrへの一意のポインタの目標値です。となります。 (私はこれを答えとして書いていません。なぜなら、これはXYの問題だと思うでしょうが、unique_ptrがラムダで使用できない理由を理解することが重要です。

EDIT 2:(!)陽気に十分、私はちょうどauto_ptrが実際にここに正しいことをするだろう、私の知る限り実現。基本的にはunique_ptrのように動作しますが、move-constructionの代わりにcopy-constructionが可能です。

+0

私は私が間違っている、のsetCallbackが値ではなく、右辺値参照によってパラメータを取得すべきだと思いますか? – Slava

+0

@Slavaそれは私が元々持っていたものですが、同じエラーが発生しました。私はrvalue参照を取ることはラムダが移動構築されることを許可する(/強制する)と思ったが、そうではないようだ。 –

答えて

14

あなたはそれは大丈夫です、ラムダを移動することができます。あなたの問題ではありませんが、std::functionをコピーできないラムダでインスタンス化しようとしています。そして:

template< class F > 
function(F f); 

functionのコンストラクタがありません:

5)fコピーでターゲットを初期化します。

std::functionためです:

はコピーコンストラクトとCopyAssignableの要件を満たしています。

functionはコピー可能である必要があるため、コピーすることもできます。そして移動のみのラムダはその要件を満たしていません。

+0

.... huh。ありがとうございます。この単純なケースでも、エラーメッセージを解析するのは非常に困難でした。 'std :: function'の代わりにuniversal-ref-qualifiedテンプレートパラメータを使うのに足りない、シグネチャを変更する方法はありますか? –

+0

@KyleStrandパラメータが何であるかは関係ありません。単に 'function'を構築することはできません。タイプ消去されたものが必要な場合は、移動可能な 'function'等価物を書く必要があります。 – Barry

+0

@バリー2つだけ言えば、すでにstd :: functionsの代替手段が既に存在します。https://github.com/Naios/Function2とhttps://github.com/potswa/cxx_function –

10

std::functionはラムダではありません。ラムダを含む任意のタイプの呼び出し可能なものから構築できるラッパーです。 std::functionにはcallable be copy-constructibleが必要です。そのため、例が失敗します。

移動のみのラムダは、次のように再度移動できます。

template<typename F> 
void call(F&& f) 
{ 
    auto f1 = std::forward<F>(f); // construct a local copy 
    f1(); 
} 

int main() 
{ 
    auto uniq = make_unique<std::string>("Blah blah blah"); 
    auto lambda = [uniq = move(uniq)]() { 
     std::cout << *uniq << std::endl; 
     }; 
// call(lambda); // doesn't compile because the lambda cannot be copied 
    call(std::move(lambda)); 
} 

Live demo

+0

Ack。私は 'std :: function'がラムダではないことを知っていましたが、ラムダは' std :: function'に変換できるので、私はその区別を明確にしていませんでした。 –

+0

元のコードにHasCallbackを実装する方法は?つまり、移動のみのラムダを移動のみのコンテナに保存するか、データ構造体だけを移動するのですか? – alpha

+0

@alphaラムダを別の型に変換せずにラムダを直接使う必要があります(ラムダを呼び出す関数がラムダをテンプレート型引数として取る必要があります)。あるいは、別の関数クラスを使用します野生の 'std :: function'のさまざまな代替方法や独自のデザインが可能です)。 –

関連する問題