By-ref参数:这是std :: thread和std :: bind之间的不一致吗?

时间:2017-05-04 17:42:12

标签: c++ c++11

std::bindstd::thread分享了一些设计原则。由于它们都存储了与传递的参数相对应的本地对象,因此如果需要引用语义,我们需要使用std::refstd::cref

void f(int& i, double d) { /*...*/ }

void g() {
    int x = 10;
    std::bind(f, std::ref(x), _1) (3.14);
    std::thread t1(f, std::ref(x), 3.14);
    //...
}

但是我对最近的个人发现很感兴趣:std::bind允许你在上面的案例中传递一个值,即使这不是人们通常想要的。

    std::bind(f, x, _1) (3.14); // Usually wrong, but valid.

然而,std::thread并非如此。以下内容将触发编译错误。

    std::thread t2(f, x, 3.14); // Usually wrong and invalid: Error!

乍一看,我认为这是一个编译器错误,但错误确实合法。由于copy decaying 要求std::thread转换int&int的构造函数的模板化版本似乎无法正确推断出参数。由30.3.1.2强加的。

问题是:为什么不要求类似于std::bind的论点?或者这种明显的不一致是什么意思?

注意:解释为什么它在下面的评论中不重复。

2 个答案:

答案 0 :(得分:6)

由于lambda的存在,

std::bind在到达时大多已经过时了。使用C ++ 14改进和C ++ 17 std::applybind的剩余用例几乎消失了。

即使在C ++ 11中,bind解决了一个问题,即lambda没有解决的问题相对罕见。

另一方面,std::thread解决了一个稍微不同的问题。它不需要bind的灵活性来“解决每个问题”,而是可以阻止通常会出现的错误代码。

bind案例中,传递给f的引用不会是x,而是对x的内部存储副本的引用。这非常令人惊讶。

void f(int& x) {
    ++x;
    std::cout << x << '\n';
};

int main() {
    int x = 0;
    auto b = std::bind(f, x);
    b();
    b();
    b();
    std::cout << x << '\n';
}

打印

1
2
3
0

最后0是原始x,而1 23x中增加的f副本。 {1}}。

使用lambda,可以明确可变存储状态和外部引用之间的差异。

auto b = [&x]{ f(x); };

VS

auto b = [x]()mutable{ f(x); };

其中一个副本x然后重复调用f,另一个副本将x的引用传递给f

bind实际上没有办法在f允许x访问std::thread的存储副本作为参考的情况下执行此操作。

对于std::thread t1([x]()mutable{ f(x); }); ,如果你想要这个可变的本地拷贝行为,你只需要使用lambda。

std::apply

事实上,我认为C ++ 11中的大多数INVOKE语法似乎都是在语言中没有C ++ 14幂lambdas和std::apply的遗产。很少有案例没有通过lambda和John's iPad <actual unique ID from the server> John's iPad <actual unique ID from the server> John's iPad <actual unique ID from the server> John's iPad <actual unique ID from the server> John's iPad <actual unique ID from the server> 解决(需要应用,因为lambda不能轻易地支持将包移入其中然后在里面进行处理)。

但是我们没有时间机器,所以我们有这些多种并行方式来表达在C ++中调用特定上下文中的内容的想法。

答案 1 :(得分:6)

据我所知,thread的基本规则与bind基本相同,但在2010年由N3090修改,以接受您已识别的约束。< / p>

使用它来平分各种贡献,我相信你正在寻找LWG issue 929

具有讽刺意味的是,其目的似乎是使thread构造函数较少受到限制。当然没有提及bind,虽然this wording was later also applied to async(LWG 1315之后的“清理”部分),所以我会说bind被遗忘。

但是很难确定,所以我建议问the committee itself