c++ {*this} inside curly braces

C++C++11

C++ Problem Overview


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 face with error:

> error: use of deleted 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;
}

C++ Solutions


Solution 1 - C++

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

Note that the code would fail to compile even with the curly braces if you made the Position(const Obj&) constructor explicit because copy-list-initialization does not allow explicit constructors to be called.

If you omit the curly braces, then semantically a temporary Position object is constructed within the function, and the return value is move constructed from that temporary. In practice, most implementations will elide the move construction, but it still requires a viable move constructor to be present, which is not the case here because it has been explicitly deleted. This is the reason your code will not compile without braces.

Using a C++17 compiler, your code will compile even without the curly braces because of guaranteed copy-elision.

Solution 2 - C++

The difference between the two is really quite subtle. C++11 introduced the feature list initialization (also sometimes called brace initialization):

Before C++11, when you want to default-construct and object o of type Obj and construct a 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 error for beginners (especially with a 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 one tries to create a Position using a constructor that takes a function pointer as its argument. In order to have a uniform 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 of your question: the difference between return {*this}; and return *this; is that the former initializes the return value directly from *this, whereas the latter first converts *this to a temporary Position object and then initializes the return value indirectly from this temporary, which fails because both the copy- and move-constructor have been explicitly deleted.

As the previous posters have noted, most compilers elide these temporary objects because they aren't really useful for anything; but this is only possible if they could be used in theory because either a copy or a move constructor is available. Because this leads to a lot of confusion (why do I need braces around my return statement? is the compiler going to elide the copy or not?), C++17 does away with these unnecessary temporaries, 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 compile fine with and without braces.

Solution 3 - C++

This is a good one! This is because return {...} means "return an object of the function's return type initialized with list initializer ...".

List initializers are described in more details here:

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

So, the difference is that {*this} calls this:

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

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

inline Position(Position const &) = delete;
inline Position(Position &&) = delete;

Solution 4 - C++

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 missing Position(Position&&) constructor, which is deleted. It is reasonable because it seems that in both cases a construction of the temporary return object is performed, which then it is to be moved to the destination. Which is impossible as move constructor is deleted. Conversely, using -std=c++17 flag it compiles in both cases (with or without curly braces) as, most probably, we are hitting the guaranteed return-value optimization of c++17. Hope this helps.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionar2015View Question on Stackoverflow
Solution 1 - C++PraetorianView Answer on Stackoverflow
Solution 2 - C++TobiasView Answer on Stackoverflow
Solution 3 - C++isp-zaxView Answer on Stackoverflow
Solution 4 - C++SandroView Answer on Stackoverflow