Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ {*this} inside curly braces

Tags:

c++

c++11

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; } 
like image 295
ar2015 Avatar asked Aug 08 '17 00:08

ar2015


People also ask

What is the role of curly braces in C program?

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.

Should curly braces appear on their own line?

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.


2 Answers

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.

like image 75
Praetorian Avatar answered Sep 28 '22 23:09

Praetorian


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.

like image 27
Tobias Avatar answered Sep 29 '22 01:09

Tobias