By-ref 参数:这是 std::thread 和 std::bind 之间的不一致吗?
By-ref arguments: is this an inconsistency between std::thread and std::bind?
std::bind
和std::thread
共享一些设计原则。由于它们都存储与传递的参数相对应的本地对象,因此如果需要引用语义,我们需要使用std::ref
或std::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!
乍一看,我认为这是一个编译器错误,但该错误确实是合法的。由于30.3.1.2强加的复制衰减要求(int
中的转换int&
),std::thread
构造函数的模板化版本似乎无法正确推导出参数。
问题是:为什么不需要类似于std::bind
的论点呢?还是有意出现这种明显的不一致?
注意:在下面的评论中解释了为什么它没有重复。
bind
返回的函数对象是为重用而设计的(即多次调用);因此,它必须将其绑定参数作为左值传递,因为您不想从所述参数中移动,否则以后的调用将看到从绑定参数中移出。(同样,您也希望函数对象作为左值调用。
这种担忧不适用于std::thread
和朋友。线程函数只会使用提供的参数调用一次。离开它们是完全安全的,因为没有其他东西会看着他们。它们实际上是临时副本,仅为新线程制作。因此,函数对象作为右值调用,参数作为右值传递。
由于lambdas的存在,std::bind
在到达时几乎已经过时了。 随着 C++14 项改进和 C++17std::apply
,bind
的剩余用例几乎消失了。
即使在 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
2
和3
是存储在f
中的x
的递增副本。
使用 lambda,可以清楚地了解可变存储状态和外部引用之间的区别。
auto b = [&x]{ f(x); };
与
auto b = [x]()mutable{ f(x); };
其中一个副本x
然后反复调用f
,另一个将x
引用传递给f
。
确实没有办法在不允许f
访问存储的x
副本作为参考的情况下使用bind
做到这一点。
对于std::thread
,如果你想要这种可变的本地复制行为,你只需使用lambda。
std::thread t1([x]()mutable{ f(x); });
事实上,我认为 C++11 中的大多数 INVOKE 语法似乎是语言中没有 C++14 次幂 lambda 和std::apply
的遗产。 很少有 lambda 和std::apply
无法解决的情况(需要应用,因为 lambda 不容易支持将包移动到其中然后将它们扩展到内部)。
但是我们没有时间机器,所以我们有这些多种并行的方式来表达在C++的特定上下文中调用某些东西的想法。
据我所知,thread
开始时的规则与bind
基本相同,但在 2010 年由 N3090 修改以接受您确定的约束。
用它来将各种贡献一分为二,我相信您正在寻找 LWG 问题 929。
具有讽刺意味的是,其意图似乎是使thread
构造函数不受约束。当然没有提到bind
,尽管这个措辞后来也适用于async
(LWG 1315之后的"清理"部分),所以我会说bind
被抛在了后面。
不过,很难确定,所以我建议问委员会本身。
- 大于65535的C++数组[size]引发不一致的溢出
- 在 C++(和 C)中进行类型转换时明显不一致
- 从 C 字符串构造 std::string 与从另一个 std::string 构造 std::string 不一致
- C++:不一致的 std::p ow( 类型 ) 定义
- 为什么 std::set::erase 与 std::set::insert 不一致?
- boost::regex 和std::regex之间的不一致?
- 如何理解 C++ std::setw 的不一致行为?
- boost::d ynamic_bitset 与 std::vector 的结果不一致<bool>?
- Visual C++ 和 gcc 之间从 std::isblank 返回不一致.哪一个错了
- By-ref 参数:这是 std::thread 和 std::bind 之间的不一致吗?
- 不一致的std :: cout行为
- boost::regex和std::regex之间的不一致
- 在 std::set 中插入对不一致(无法识别 <pair>.second)
- std::push_back和insert(end(),x)之间的矢量不一致崩溃
- 与 std::无法打印特定数字不一致
- c++std::normal_distribution在从文件恢复时给出不一致的随机数
- std::regex_search与gcc 4.9.1的结果不一致
- std::is_convertible 与 std::function 不一致
- std::设置不一致的运算符<
- std::string和字符串字面值不一致