Regarding the behavior of automatic conversion of C ++ type to std :: thread

I have two classes cl1 and cl2 and cl1 has a constructor that takes the cl2& parameter. I have three functions: one takes cl1 as a parameter, one takes cl1&& as a parameter, and one with cl1& as a parameter.

 #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(ie 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(); } 

On ideone.com: http://ideone.com/MPZc4C , as I was going to ask this question, the example works. But with g ++ - 4.7, I get an error, for example:

 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 

I really don't know if there are any implementation problems or code. I just study threads and stuff in C ++, so there is no practical reason why I do this. Please let me know what the problem is, and also if I'm right in the comments on the code. (The comments β€œThis works ...” in the code mean that they are good when called with an object as a parameter (rather than a reference to it) from main ().)

+4
source share
1 answer

Paragraph Β§ 30.3.1.2/3 of the C ++ Standard states:

"Required: F and each Ti in Args must satisfy MoveConstructible. INVOKE ( DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...) (20.8. 2) is a valid expression. "

The expression DECAY_COPY(x) defined in 30.2.6:

"In several places in this section, the DECAY_COPY(x) operation is used. All such uses mean calling the DECAY_COPY(x) function and using the result, where decay_copy is defined as follows:"

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

Since the decay operation removes the cv qualifiers from the object, cl2 must be a universally valid conversion constructor or conversion operator from type cl1 type cl1 . To test this, the std::thread transport mechanism apparently generates rvalue references to cl1 and tries to get c2 instances from them. This fails because rvalue links cannot be bound to a non-const reference link in the conversion constructor.

If you change the signature of your constructor from cl1(cl2& ob1) to cl1(cl2 const& ob1) , it works with GCC 4.7.2 because rvalue links can be bound to const lvalue links.

+5
source

All Articles