我对gcc 4.5.2有一个有趣的问题。以下代码
#include<thread>
#include<iostream>
using std::cout;
void foo(int a){
cout<<a;
}
template <typename T>
void goo(void (*fn)(T),T c){
fn(c);
}
int main(void)
{
std::thread TH;
void (*ptr)(int)=foo;
TH= std::thread(goo<int>,ptr,1);
TH.join();
return 0;
}
..将无法使用error: cannot bind ‘void(void (*)(int), int)’ lvalue to ‘void (&&)(void (*)(int), int)’
在gcc 4.5.2上进行编译,然后是第二个错误initializing argument 1 of ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void(void (*)(int), int), _Args = {void (*&)(int), int}]’
但是,此代码在删除template
时进行编译,并且还使用gcc 4.7.0(template
到位)进行编译。
即使这是编译器问题,有人可以解释这样的错误意味着什么吗?我很乐意找到一种方法来进行绑定(即使这是gcc 4.7中的自动)。
答案 0 :(得分:6)
GCC的第一个错误是在“完美转发” - 值类别扣除期间,它认为goo<int>
是一个右值。但goo<int>
实际上是一个左值。因此,不应将_Callable
推断为void(void (*)(int), int)
,而应将其推断为void(&)(void (*)(int), int)
。然后引用折叠会产生左值引用作为参数类型,而不是void(&&)(void (*)(int), int)
,就像它错误地对您的GCC版本一样。
在参数的实际初始化期间,它还错误地拒绝了函数左值初始化rvalue引用参数(我不记得GCC4.5发布时工作草案的状态是什么 - 但可能草案已经有了那时候形成不良)。对于函数类型表达式,标准允许使用函数类型的左值初始化函数类型的右值引用。
将模板ID解析为函数左值非常复杂,因此GCC出错也不会让我感到惊讶(请参阅http://llvm.org/bugs/show_bug.cgi?id=7505和http://llvm.org/bugs/show_bug.cgi?id=7505以获取看似简单的规则交互的两个示例的东西)。
答案 1 :(得分:4)
左值是表示可以获取其地址的对象的表达式。例如,基元类型的赋值表达式的左侧必须是左值:int i; i = 3;
是正常的,而5 = 3
则不是。&i
。不同之处在于&5
可以,但goo<int>
不是。
goo
是函数类型的左值,但函数类型的表达式在C和C ++中基本上是无用的。通过获取地址,它们总是被转换为函数指针。结果指针不是左值,因为它将获取地址的地址。
G ++中的错误在于隐含地采用地址。显然,当您传递非模板goo<int>
但在传递&
之后,转换发生在模板扣除之前。您需要更早发生,因此构造函数不会尝试接收引用。
一元TH= std::thread(&goo<int>,ptr,1);
运算符足以强制转换:
{{1}}