Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

visual studio implementation of "move semantics" and "rvalue reference"

I came across a Youtube video on c++11 concurrency (part 3) and the following code, which compiles and generates correct result in the video.

However, I got a compile error of this code using Visual Studio 2012. The compiler complains about the argument type of toSin(list<double>&&). If I change the argument type to list<double>&, the code compiled.

My question is what is returned from move(list) in the _tmain(), is it a rvalue reference or just a reference?

#include "stdafx.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <list>
#include <algorithm>
using namespace std;

void toSin(list<double>&& list)
{
    //this_thread::sleep_for(chrono::seconds(1));
    for_each(list.begin(), list.end(), [](double & x)
    {
        x = sin(x);
    });

    for_each(list.begin(), list.end(), [](double & x)
    {
        int count = static_cast<int>(10*x+10.5);
        for (int i=0; i<count; ++i)
        {
            cout.put('*');
        }
        cout << endl;
    });
}    

int _tmain(int argc, _TCHAR* argv[])
{
    list<double> list;

    const double pi = 3.1415926;
    const double epsilon = 0.00000001;
    for (double x = 0.0; x<2*pi+epsilon; x+=pi/16)
    {
        list.push_back(x);
    }

    thread th(&toSin, /*std::ref(list)*/std::move(list));
    th.join();    

    return 0;
}
like image 408
exponential Avatar asked Feb 04 '15 20:02

exponential


People also ask

What is the use of rvalue references and move semantics?

In other words, the rvalue references and move semantics allows us to avoid unnecessary copies when working with temporary objects. We do not want to copy the temporary which will go away.

What are Rvalue references and why are they important?

This might seem useless at a first glance. However rvalue references pave the way for the implementation of move semantics, a technique which can significantly increase the performance of your applications.

What is move semantics in Java?

Move semantics enables you to write code that transfers resources (such as dynamically allocated memory) from one object to another. Move semantics works because it enables resources to be transferred from temporary objects that cannot be referenced elsewhere in the program.

What is the difference between lvalue and rvalue in JavaScript?

By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues). You can pass an object to a function that takes an rvalue reference unless the object is marked as const.


1 Answers

This appears to be a bug in MSVC2012. (and on quick inspection, MSVC2013 and MSVC2015)

thread does not use perfect forwarding directly, as storing a reference to data (temporary or not) in the originating thread and using it in the spawned thread would be extremely error prone and dangerous.

Instead, it copies each argument into decay_t<?>'s internal data.

The bug is that when it calls the worker function, it simply passes that internal copy to your procedure. Instead, it should move that internal data into the call.

This does not seem to be fixed in compiler version 19, which I think is MSVC2015 (did not double check), based off compiling your code over here

This is both due to the wording of the standard (it is supposed to invoke a decay_t<F> with decay_t<Ts>... -- which means rvalue binding, not lvalue binding), and because the local data stored in the thread will never be used again after the invocation of your procedure (so logically it should be treated as expiring data, not persistent data).

Here is a work around:

template<class F>
struct thread_rvalue_fix_wrapper {
  F f;
  template<class...Args>
  auto operator()(Args&...args)
  -> typename std::result_of<F(Args...)>::type
  {
      return std::move(f)( std::move(args)... );
  }
};
template<class F>
thread_rvalue_fix_wrapper< typename std::decay<F>::type >
thread_rvalue_fix( F&& f ) { return {std::forward<F>(f)}; }

then

thread th(thread_rvalue_fix(&toSin), /*std::ref(list)*/std::move(list));

should work. (tested in MSVC2015 online compiler linked above) Based off personal experience, it should also work in MSVC2013. I don't know about MSVC2012.

like image 193
Yakk - Adam Nevraumont Avatar answered Oct 25 '22 07:10

Yakk - Adam Nevraumont