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; }
In programming, curly braces (the { and } characters) are used in a variety of ways. In C/C++, they are used to signify the start and end of a series of statements. In the following expression, everything between the { and } are executed if the variable mouseDOWNinText is true. See event loop.
We strongly recommend you format your curly brackets following Java conventions: Do not put a new line before an opening curly bracket. Always start a new line after an opening curly bracket. A closing curly bracket should always belong on a separate line, except for else statements.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With