简体   繁体   中英

Regarding C++ automatic type conversion behaviour in std::thread

I have a created two classes cl1 and cl2 and cl1 has a constructor that takes a cl2& parameter. I have three functions, one taking cl1 as parameter, one taking cl1&& as parameter and one with cl1& as the 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(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();                                              
}

At 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 like :

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 don't really know if it's any problem with the implementation, or the code.. I am just learning about threads and stuff in C++, so there's no practical reason why I am doing this. Please let me know what the issue is and also if I am correct in the comments of the code. (The comments "This works..." in the code mean that they are good when called with the object as the parameter(not a reference to it) from main().)

Paragraph § 30.3.1.2/3 of the C++ Standard says:

"Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements. INVOKE ( DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...) (20.8.2) shall be a valid expression" .

The expression DECAY_COPY(x) is in turned defined in 30.2.6:

"In several places in this Clause the operation DECAY_COPY(x) is used. All such uses mean call the function decay_copy(x) and use 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 cv-qualifiers from the object, there needs to be a universally valid conversion constructor or conversion operator from type cl1 to type cl2 . To check this, the forwarding machinery of std::thread apparently generates rvalue references to cl1 and tries to get instances of c2 from them. This fails because rvalue references cannot be bound to the non-const lvalue reference in your converting constructor.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM