How to avoid unnecessary instances using rvalue references in C ++

I would like to create a custom container Container that stores data in separate arrays. However, to facilitate iterations over the container, I provide a “view” in the container by overloading operator[] and returning a single Value structure that contains all the container variables as references to the actual container. This is what I got so far:

 #include <iostream> using namespace std; struct Value { Value(int& data) : data_(data) { } int& data() { return data_; } int& data_; }; struct Container { Value makeValue(int i) { return Value(data_[i]); } // EDIT 1 Value&& operator[](int i) { // return std::forward<Value>(Value(data_[i])); return std::forward<Value>(makeValue(i)); // EDIT 1 } int data_[5] = {1, 2, 3, 4, 5}; }; int main(int, char**) { // Create and output temporary Container c; cout << c[2].data() << endl; // Output: 3 - OK! // Create, modify and output copy Value v = c[2]; cout << v.data() << endl; // Output: 3 - OK! v.data() = 8; cout << v.data() << endl; // Output: 8 - OK! // Create and output reference Value&& vv = c[2]; cout << vv.data() << endl; // Output: 8 - OK, but weird: // shouldn't this be a dangling reference? cout << vv.data() << endl; // Output: 468319288 - Bad, but that expected... } 

The code above works as far as I can tell, but I'm wondering if I am using a better approach here:

  • Is it correct to return Value as a reference to rvalue if I want to avoid unnecessary copying?
  • Is std::forward used correctly? Should I use std::move (both will work in this example) or something else?
  • The result of the compiled program is indicated in the comments. Is there a way to avoid dangling links when I declare Value&& vv... (or even deny it syntactically)?

EDIT 1

I made a small change in the source code so that the Value instance is not created directly in the operator[] method, but in another helper function. Will this change anything? Should I use the makeValue(int i) method as shown, or do I need to use std::move / std::forward here?

+4
source share
2 answers

Is it correct to return a value as a reference to rvalue if I want to avoid unnecessary copying?

Not. Returning rvalue links from something that is not an helper of type std::move or std::forward is incorrect. Rvalue links are still links. Returning a reference to a temporary or local variable has always been wrong, and still not. These are the same old C ++ rules.

Is std::forward used correctly? Should I use std::move (both will work in this example) or something else?

The answer to the previous question of the kind makes this question controversial.

The output of the compiled program is indicated in the comments. Is there a way to avoid dangling links when I declare Value&& vv... (or even deny it syntactically)?

This is not part of Value&& vv = c[2]; which creates a freeze link. This is operator[] : see the answer to the first question.

In this case, Rvalue links do not change much. Just do what you always did:

 Value operator[](int i) { return Value(data_[i]); } 

Any compiler that is worth using will optimize it to directly initialize the return value without any copy or move or anything else. With dumb / useless / weird / experimental compilers, it will move in the worst case (but why would anyone use such a thing for serious things?).

So, the string Value v = c[2]; will be initialized directly v . String Value&& vv = c[2]; initializes the temporary and binds it to the rvalue reference variable. They have the same property as const& , and they extend the lifetime of a temporary link to the lifetime of a link, so it won’t hang out.

In general, the same old C ++ always works and still produces results that are correct and efficient. Do not forget about it.

+3
source

Returning a reference to temporary objects, even if it is a reference to an r-value, is always incorrect! By the time you access the object, it will disappear. In this case, it also does not do what you want, anyway: if you want to avoid unnecessary copies, you have one return statement that returns a temporary one! Copy / move elision will take care that the object is not copied:

 Value operator[](int i) { return Value(data_[i]); } 

Passing a temporary object through a function prohibits copying / moving, not copying / moving, even less than moving.

+3
source

Source: https://habr.com/ru/post/1498969/


All Articles