使用ref arg获取lambda的std :: thread无法编译

Dan*_*und 12 c++ generics lambda c++14

我正在阅读C++并发性.第2.4章描述了parallell_accumulate算法.

我尝试 - 作为一个学习实验 - 用一般的lambda替换那里使用的仿函数.

我已将编译错误提炼为:

#include <thread>

template <typename T>
struct f {
    void operator() (T& result) { result = 1;}
};

int main() {
    int x = 0;
    auto g = [](auto& result) { result = 1; };

    std::thread(f<int>(), std::ref(x));  // COMPILES
    std::thread(g, std::ref(x));         // FAILS TO COMPILE
}
Run Code Online (Sandbox Code Playgroud)

错误消息:

 In file included from /usr/include/c++/4.9/thread:39:0,
                 from foo.cpp:1:
/usr/include/c++/4.9/functional: In instantiation of ‘struct std::_Bind_simple<main()::<lambda(auto:1&)>(std::reference_wrapper<int>)>’:
/usr/include/c++/4.9/thread:140:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = main()::<lambda(auto:1&)>&; _Args = {std::reference_wrapper<int>}]’
foo.cpp:13:31:   required from here
/usr/include/c++/4.9/functional:1665:61: error: no type named ‘type’ in ‘class std::result_of<main()::<lambda(auto:1&)>(std::reference_wrapper<int>)>’
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/4.9/functional:1695:9: error: no type named ‘type’ in ‘class std::result_of<main()::<lambda(auto:1&)>(std::reference_wrapper<int>)>’
         _M_invoke(_Index_tuple<_Indices...>)
         ^
Run Code Online (Sandbox Code Playgroud)

我的编译器版本

$ g++ --version
g++ (Ubuntu 4.9.1-16ubuntu6) 4.9.1
Run Code Online (Sandbox Code Playgroud)

为什么编译失败的lambda而不是functor?

编辑:我怎样才能通过一个普通的lambda实现仿函数所做的事情(分配给一个ref)?

T.C*_*.C. 18

同一主题的另一个变体是模板参数推导不能通过转换查看.

operator()f<int>

void operator() (int& result);
Run Code Online (Sandbox Code Playgroud)

当你传递reference_wrapper<int>给它时,operator int &调用转换函数(),产生一个可以绑定的引用result.

operator()您的通用拉姆达的是

template<class T> void operator() (T& result) const;
Run Code Online (Sandbox Code Playgroud)

如果它传递了reference_wrapper左值,它将推断T为a reference_wrapper,然后无法编译赋值.(分配给一个reference_wrapper重新设置"参考"而不是影响该值.)

但它甚至在此之前就失败了,因为标准要求你传递的内容std::thread必须可以用prvalues调用 - 而非const的左值引用不能绑定到prvalue.这是您看到的错误 - result_of包含no,type因为您的仿函数不能为参数类型调用.如果您尝试这样做g(std::ref(x));,clang 会产生一个相当明显的错误:

main.cpp:16:5: error: no matching function for call to object of type '(lambda at main.cpp:11:14)'
    g(std::ref(x));
    ^
main.cpp:11:14: note: candidate function [with $auto-0-0 = std::__1::reference_wrapper<int>] not viable: expects an l-value for 1st argument
    auto g = [](auto& result) { result = 1; };         
    ^
Run Code Online (Sandbox Code Playgroud)

您可能应该考虑通过引用捕获相关的本地:

auto g = [&x]() { x = 1; };
Run Code Online (Sandbox Code Playgroud)

或者,如果由于某种原因,您必须使用泛型lambda,那么您可以使用reference_wrapperby值(或通过const引用),然后使用get()以下命令将其解包:

 auto g = [](auto result) { result.get() = 1; };
Run Code Online (Sandbox Code Playgroud)

或者可能添加一个std::bind将打开reference_wrappers的东西,这样可以让模板参数推断做正确的事(帽子提示@Casey):

 std::thread(std::bind(g, std::ref(x)));
Run Code Online (Sandbox Code Playgroud)

或者可能免除这种reference_wrapper废话并写下你的lambda来取代非拥有指针:

auto g = [](auto* result) { *result = 1; };
std::thread(g, &x);
Run Code Online (Sandbox Code Playgroud)


Cas*_*sey 5

还有的通过"的invoke(...)"系列的功能涉及到参数传递种种问题std::async,std::bind,std::thread::thread.如果你想使用一个重载的函数名,或者传递一个左值引用,或者天堂禁止通过引用传递右值,那么你将会遇到困难.你会来到这里,我们其中一个已经学会相关咒语的人会把它传给你.希望你下次出现时会记住它.

我认为自C++ 14以来的最佳实践是通过自己处理参数来避免参数传递奇怪,并且总是给INVOKE函数一个零参数函子,它封装了实际目标函数所需的参数.通过自己动手,您可以获得您想要的语义,而无需了解每个怪癖和变通方法以及INVOKE系列函数接口的细微差别.C++ 14广义lambda捕获使得封装任何类型的函数和参数集非常简单.

在您的情况下,这种方法将导致:

#include <thread>

template <typename T>
struct f {
    void operator() (T& result) { result = 1;}
};

int main() {
    int x = 0;
    auto g = [](auto& result) { result = 1; };

    std::thread([&]{ return f<int>{}(x); });
    std::thread([&]{ return g(x); });
}
Run Code Online (Sandbox Code Playgroud)

它完全按预期执行,更具可读性.

std::reference_wrapper在TR1时代,当我们需要它来传递参考时,它是伟大的std::bind,但它的辉煌岁月已过去,我认为现代C++中最好避免它.