启动std :: thread时,无法将(函数指针)左值绑定到(函数指针)rvalue?

时间:2012-05-20 05:52:17

标签: c++ multithreading templates c++11

我对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中的自动)。

2 个答案:

答案 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=7505http://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}}

http://ideone.com/mI05j