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 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;
}
Solution 1:
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:
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:
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;