2016-05-02 8 views
5

私はちょうど与えられたラムダでstd::functionがどのように構築されるのかちょっと混乱します。 std::functionのコンストラクタはhereと記載されています。実際にラムダを捕捉するのにどちらが使われますか?それはtemplate< class F > function(F f);ですか?私はstd::functionを構成することができないように見えますが、コピー可能でないオブジェクトをキャプチャするラムダを使用しています。これはラムダ捕獲になぜ必要なのですか?ラムダ用にstd ::関数がどのように構築されました

// fu is an object of type std::future 
std::function f = [future=std::move(fu)]() {...} // compile error 

// foo is an object of type int 
std::function f = [foo=std::move(foo)]() {...} // compile ok 
+0

同様の質問:http://stackoverflow.com/questions/25421346/how-移動後のキャプチャラムダ式を作成するには – marcinj

答えて

3

簡潔な答えは、標準状態でコピー可能な関数オブジェクトのみをstd::functionに保存できるということです。これは不満足です:なぜですか?

std::functionはコピー可能なタイプです。

標準では、コピーするとその内容もコピーされることが記載されています。

「しかし、私は決してコピーしません。どうしてコピーする必要がありますか? std::functionのインスタンスは、をどのようにコピーするのですか?その内容は決してそうでない場合でもコピーします。通常、タイプ消去と呼ばれる手法を使用します。ここ

玩具例である:

struct invoke_later { 
    struct i_impl { 
    virtual ~i_impl() {} 
    virtual void invoke() const = 0; 
    virtual std::unique_ptr<i_impl> clone() const = 0; 
    }; 
    template<class T> 
    struct impl:i_impl { 
    T t; 
    ~impl() = default; 
    void invoke() const override { 
     t(); 
    } 
    impl(T&& tin):t(std::move(tin)) {} 
    impl(T const& tin):t(tin) {} 
    virtual std::unique_ptr<i_impl> clone() const { 
     return std::make_unique<impl>(t); 
    }; 
    }; 
    std::unique_ptr<i_impl> pimpl; 

    template<class T, 
    // SFINAE suppress using this ctor instead of copy/move ctors: 
    std::enable_if_t< !std::is_same<std::decay_t<T>, invoke_later>{}, int>* =0 
    > 
    invoke_later(T&& t): 
    pimpl(std::make_unique<impl<std::decay_t<T>>(std::forward<T>(t))) 
    {} 

    invoke_later(invoke_later&&)=default; 
    invoke_later(invoke_later const&o): 
    pimpl(o.pimpl?o.pimpl->clone():std::unique_ptr<i_impl>{}) 
    {} 

    ~invoke_later() = default; 

    // assignment goes here 

    void operator() const { 
    pimpl->invoke(); 
    } 
    explicit operator bool() const { return !!pimpl; } 
}; 

上記は、std::function<void()>のおもちゃの一例です。

pimpl->clone()の方法では、コピーの操作はであり、コピーはです。と決して呼ばれない場合でも、をコンパイルする必要があります。

std仕様の作成者は、上記の技術を認識しており、その限界を知り、単にそれを使用してstd::functionを実装することを許可したかったのです。さらに、彼らはstd::functionの簡単な操作が予測可能な方法で動作することを望んでいました。コピーできないコンテンツでは、std::functionは何をコピーすべきですか?

この問題は、shared_ptrにあなたの状態をラップすることで回避できます。次に、std::functionのコピーは、コピーではなく、あなたの状態への共有参照を単に保存します。今

template<class F> 
auto shared_state(F&& f) { 
    return [pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f))] 
    (auto&&... args)->decltype(auto) { 
     return (*pf)(decltype(args)(args)...); 
    }; 
} 

std::function<Sig> f = shared_state([future=std::move(fu)]() {...}); 

をコンパイルして動作します。

代わりの方法は、コピー不可能なものをstd::functionにして、それをstd::functionの代わりに使用することです。

最後に、futureで作業WHE、shared_futureはコピー可能futureタイプで、shared_stateを行うよりも安くなることがあります。

std::function<void()> f = [fu=fu.share()]{ /* code */ }; 
+0

特に 'future'sの場合、' .share() 'は安価かもしれません。 –

+0

@ t.c。良い点:段落を追加しました。 – Yakk

1

値だけ移動専用オブジェクトをキャプチャラムダは、移動のみ、それが前記オブジェクトを含むので理にかなってそれ自体になります。

ただし、std::functionは、copy-constructiblecopy-assignableである必要があります。つまり、コピー可能なオブジェクトのみを含むことができます。

関連する問題