C ++ {* this} inside curly braces

The following code compiles fine:

g++ -std=c++11 test.cpp -Wall -Wextra -Wfatal-errors && ./a.out 

However, if I remove the curly braces from {*this} and use *this instead, I will encounter an error:

error: using the remote function 'Obj :: Position :: Position (Obj :: Position & &)

What is the difference between {*this} and *this ?

 class Obj { template<bool> friend class Position; double data; public: class Position { const Obj& ref; public: inline Position(const Obj& ref): ref(ref){} inline Position(Position const &) = delete; inline Position(Position &&) = delete; }; inline Obj(){} inline Obj(const double &data): data(data){} inline auto get_pos() const-> Position{return {*this};} /* <--- here */ inline auto get_pos()-> Position{return {*this};} }; int main() { return 0; } 
+49
c ++ c ++ 11
Aug 08 '17 at 0:13
source share
4 answers

The difference between them is really quite subtle. C ++ 11 introduced the initialization of a list of functions (also sometimes called initializing brackets):

Before C ++ 11, when you want to build a default construct and an o object of type Obj and build Position p from o , you had to write

 Obj o; // default construct o Obj::Position p(o); // construct p using Position(Obj const&) 

A common mistake for beginners (especially with the Java background) was to try to write this:

 Obj o(); // mistake: declares a function o returning an Obj Obj::Position p(o); // error: no constructor takes a function 

The first line declares a function , and the second tries to create a Position using a constructor that takes a pointer to a function as an argument. To have the same initializer syntax, C ++ 11 introduced list initialization:

 Obj oo{}; // new in C++11: default construct o of type Obj Obj::Position p1(oo); // possible before (and after) C++11 Obj::Position p2{oo}; // new in C++11: construct p2 using Position(Obj const&) 

This new syntax also works in return -statements, and this leads to the answer to your question: the difference between return {*this}; and return *this; the former initializes the return value directly from *this , while the latter first converts *this to a temporary Position object, and then initializes the return value indirectly from this temporary , which fails, because copy-and move-constructor have been explicitly deleted.

As previous posters noted, most compilers exclude these temporary objects because they are of no use; but this is only possible if they can be used in theory, because either an instance or a movement mechanism is available. Because this leads to a lot of confusion (why do I need parentheses around my return statement? Is the compiler giving a copy or not?), C ++ 17 eliminates these unnecessary time series and initializes the return value directly in both cases ( return {*this}; and return *this ).

You can try this using a compiler that supports C ++ 17. In clang 4.0 or gcc 7.1 you can pass --std=c++1z , and your code should be compiled with and without parentheses.

+13
Aug 10 '17 at 21:13
source share

When braces are present, you copy-list-initializing the return value, the copy / move constructor is not involved. The return value is built in place using the Position(const Obj&) constructor Position(const Obj&) .

Note that the code will not be able to compile even with curly braces if you created the Position(const Obj&) explicit constructor, because initializing the list of copies does not allow calling explicit constructors.

If you omit curly braces, then the semantically temporary Position object is created inside the function, and the return value is moved from this temporary. In practice, most implementations will overcome the displacement construct, but this still requires a viable displacement constructor, which is not the case here because it has been explicitly removed. It is for this reason that your code will not compile without braces.

Using the C ++ 17 compiler, your code will compile even without curly braces due to guaranteed copy-permission .

+39
Aug 08 '17 at 0:24
source share

This one good! This is due to the fact that return {...} means "return an object of the returned function type, initialized using the list initializer ...".

List initializers are described in more detail here:

http://en.cppreference.com/w/cpp/language/list%20initialization

So the difference is that {*this} causes this:

 inline Position(const Obj& ref): ref(ref){} 

While *this tries to convert Obj& to Position using explicitly deleted assignment operators (pre C ++ 11, they would have to be made private , and you will get an even more confusing error message if list initializers are available ...):

 inline Position(Position const &) = delete; inline Position(Position &&) = delete; 
+13
Aug 08 '17 at 0:34 on
source share

frankly using your class and the following main ():

 int main() { Obj o1; cout<<"get position"<<'\n'; Obj::Position pos= o1.get_pos(); cout.flush(); return 0; } 

it does not compile (gcc / mingw) in both cases (-std = C ++ 14), with or without curly braces, and it complains about the absence of the Position (Position & &) constructor, which is deleted. This is reasonable because it seems that in both cases a temporary return object is being created, which then needs to be moved to the destination. This is not possible because the move constructor is removed. And vice versa, using the -std = C ++ 17 flag, it compiles in both cases (with or without curly braces), since most likely we are trying to achieve guaranteed optimization of the returned C ++ 17 value. I hope this helps.

-2
Aug 08 '17 at 17:13
source share



All Articles