Why does get helper of std::tuple return rvalue reference instead of value

混江龙づ霸主 提交于 2019-12-01 17:44:24

Your example of how this can be used to create a dangling reference is very interesting, but it's important to learn the correct lesson from the example.

Consider a much simpler example, that doesn't have any && anywhere:

const int &x = vector<int>(1) .front();

.front() returns an &-reference to the first element of the new constructed vector. The vector is immediately destroyed of course and you are left with a dangling reference.

The lesson to be learned is that using a const-reference does not, in general, extend the lifetime. It extends the lifetime of non-references. If the right hand side of = is a reference, then you have to take responsibility for lifetimes yourself.

This has always been the case, so it wouldn't make sense for tuple::get to do anything different. tuple::get is permitted to return a reference, just as vector::front has always been.

You talk about move and copy constructors and about speed. The fastest solution is to use no constructors whatsoever. Imagine a function to concatenate two vectors:

vector<int> concat(const vector<int> &l_, const vector<int> &r) {
    vector<int> l(l_);
    l.insert(l.end(), r.cbegin(), r.cend());
    return l;
}

This would allow an optimized extra overload:

vector<int>&& concat(vector<int>&& l, const vector<int> &r) {
    l.insert(l.end(), r.cbegin(), r.cend());
    return l;
}

This optimization keeps the number of constructions to a minimum

   vector<int> a{1,2,3};
   vector<int> b{3,4,5};
   vector<int> c = concat(
     concat(
       concat(
          concat(vector<int>(), a)
       , b)
      , a
   , b);

The final line, with four calls to concat, will only have two constructions: The starting value (vector<int>()) and the move-construct into c. You could have 100 nested calls to concat there, without any extra constructions.

So, returning by && can be faster. Because, yes, moves are faster than copies, but it's even faster still if you can avoid both.

In summary, it's done for speed. Consider using a nested series of get on a tuple-within-a-tuple-within-a-tuple. Also, it allows it to work with types that have neither copy nor move constructors.

And this doesn't introduce any new risks regarding lifetime. The vector<int>().front() "problem" is not a new one.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!