关于std :: thread中的C ++自动类型转换行为

时间:2013-01-16 17:57:41

标签: c++ multithreading c++11 rvalue-reference

我创建了两个类cl1cl2cl1有一个带cl2&参数的构造函数。 我有三个函数,一个以cl1为参数,一个以cl1&&为参数,另一个以cl1&为参数。

#include <thread>
#include <iostream>

class cl1;
class cl2;


class cl2 {
public:
    int y;
    cl2(int y) : y(y)   {}                  //ctor
};

class cl1 {
public:
    int x;
    cl1(int x) : x(x) {}                 //ctor
    cl1(cl2& ob1) : x(ob1.y * 2) {}      //ctor for automatic conversion of cl2& to cl1, x = y*2
};

void do_work_with_cl(cl1 ob) {              //This works as usual by actually copying the object through the conversion constructor
    std::cout << "The x of ob is " << ob.x << std::endl;
}

void do_work_with_cl_rref(cl1&& ob) {       //I guess this works because it takes an rvalue and the automatic
                                            //conversion ctor of cl1 does just that
    std::cout <<"Inside the function that takes cl1 as rvalue, x of ob is"  << ob.x << std::endl;
}

void do_work_with_cl_lref(cl1& ob) {        //This doesn't work as ob is non-const lvalue reference
    std::cout << "lvalue referenced but the object created through implicit conversion is temporary(i.e rvalue)" << std::endl;
}   


int main() {
    //Normal non-threaded calls
    cl2 ob(100);                //create a cl2 object
    do_work_with_cl(ob);            //This is ok
    do_work_with_cl_rref(ob);   //This too works
    //do_work_with_cl_lref(ob)  //This fails, as suspected

    std::cout << "Thread part" << std::endl

    //Now calling the functions through a thread
    std::thread t1(do_work_with_cl_rref, ob);   //Thought this could work here, but doesn't
                                                //The other functions also don't work, but I can understand why.
    t1.join();                                              
}

在ideone.com上:http://ideone.com/MPZc4C,因为我要问这个问题,示例有效。但是使用g ++ - 4.7我得到一个错误:

In file included from /usr/include/c++/4.7/ratio:38:0,
             from /usr/include/c++/4.7/chrono:38,
             from /usr/include/c++/4.7/thread:38,
             from main.cpp:1:
/usr/include/c++/4.7/type_traits: In instantiation of ‘struct std::_Result_of_impl<false, false, void (*)(cl1&&), cl2>’:
/usr/include/c++/4.7/type_traits:1857:12:   required from ‘class std::result_of<void (*(cl2))(cl1&&)>’
/usr/include/c++/4.7/functional:1563:61:   required from ‘struct std::_Bind_simple<void (*(cl2))(cl1&&)>’
/usr/include/c++/4.7/thread:133:9:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(cl1&&); _Args = {cl2&}]’
main.cpp:13:44:   required from here
/usr/include/c++/4.7/type_traits:1834:9: error: invalid initialization of reference of type     ‘cl1&&’ from expression of type ‘cl2’
make: *** [main.o] Error 1

我真的不知道它是否与实现或代码有任何问题。我只是在学习C ++中的线程和东西,所以没有实际的理由我为什么这样做。请告诉我这是什么问题,如果我在代码的评论中是正确的。 (代码中的“This works ......”注释表示当从main()中将对象作为参数(不是对它的引用)调用它们时它们是好的。)

1 个答案:

答案 0 :(得分:5)

C ++标准第30.3.1.2/3段说:

“要求:F和Args中的每个Ti应满足MoveConstructible要求.INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)(20.8.2)应为有效表达式”

表达式DECAY_COPY(x)已在30.2.6中定义:

“在本条款的几个地方使用了操作DECAY_COPY(x)。所有这些用法意味着调用函数decay_copy(x)并使用结果,其中decay_copy定义如下:“

template <class T> typename decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }

由于decay操作会从对象中删除cv限定符,因此需要有一个通用有效转换构造函数或转换运算符,从类型cl1到{{1} }}。为了检查这一点,cl2的转发机制显然会生成对std::thread的右值引用,并尝试从中获取cl1的实例。这是失败的,因为rvalue引用不能绑定到转换构造函数中的非const 左值引用。

如果将构造函数的签名从c2更改为cl1(cl2& ob1),则它适用于GCC 4.7.2,因为rvalue引用可以绑定到cl1(cl2 const& ob1)的左值引用。