DawidPi
DawidPi

Reputation: 2365

std::thread and rvalue reference

I wanted to have some kind of delegator class. Shortened version of my approach is below and it's main functionality is to start new thread doing some thing (in this example it prints text every second):

void Flusher::start(){
    m_continue.store(true);

    m_thread = std::thread([](std::atomic<bool>& shouldContinue){
        while(shouldContinue.load()){
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << "sec passed" << std::endl;
        }}, std::ref<std::atomic<bool>>(m_continue)
    );
}

My concern is, that std::thread constructor has following signature:

template< class Function, class... Args > 
explicit thread( Function&& f, Args&&... args );

So it takes rvalue reference as the first and second argument. If it's so, then I should not use shouldContinue after passing it to the std::thread constructor as it was moved.

Of course I want to have control over this function and therefore I want to use shouldContinue in a caller thread to stop called function. For obvious reasons I do not want to make this variable global.

I think, that std::ref makes some magic there, but I am still not sure how does it work (I saw std::ref in some examples when creating new thread).

I tried to not care at all about the fact, this is rvalue reference and I used shouldContinue later on and nothing crashed, but I fear this is simply undefined behavior. Could anyone tell if above code is correct and if not, how to do this correctly?

Upvotes: 1

Views: 1606

Answers (1)

basav
basav

Reputation: 1495

There is a special type deduction rule when && is used with templates.

Check this out for a really good explanation:

http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c/

template <class T>
void func(T&& t) {
}

"When && appears in a type-deducing context, T&& acquires a special meaning. When func is instantiated, T depends on whether the argument passed to func is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to U:"

func(4);            // 4 is an rvalue: T deduced to int

double d = 3.14;
func(d);            // d is an lvalue; T deduced to double&

float f() {...}
func(f());          // f() is an rvalue; T deduced to float

int bar(int i) {
  func(i);          // i is an lvalue; T deduced to int&
}

Also, reference collapsing rule is a good read.

Upvotes: 1

Related Questions