c大括号内的{* this}

下面的代码编译得很好:

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

但是,如果我从{*this}删除大括号,并使用*this替代,我将面对错误:

错误:使用已删除的函数'Obj :: Position :: Position(Obj :: Position &&)'

{*this}*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 ++ 11引入了特征列表初始化 (有时也被称为括号初始化 ):

在C ++ 11之前,当你想默认构造Objtypes的对象o并从o构造一个Position p时,你必须写

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

初学者(特别是Java背景)的一个常见错误是试图写这个:

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

第一行声明了一个函数 ,第二行试图使用一个构造函数来创build一个Position ,该构造函数接受一个函数指针作为它的参数。 为了有一个统一的初始化语法,C ++ 11引入了列表初始化:

 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&) 

这个新的语法也适用于return -statements,这导致了你的问题的答案: return {*this};return *this; 是前者直接*this初始化返回值,而后者则首先将*this转换成一个临时的Position对象,然后从这个临时对象间接初始化返回值,由于copy和move构造函数都被明确地删除。

正如前面的海报所指出的那样,大多数编译器将这些临时对象删除,因为它们对于任何事情都没有什么用处。 但是这只有在理论上可以使用时才有可能,因为复制或移动构造函数都是可用的。 因为这会导致很多混淆(为什么我的return语句需要大括号?是编译器会不会去复制?),C ++ 17会消除这些不必要的临时对象,并直接初始化返回值两种情况( return {*this};return *this )。

您可以使用支持C ++ 17的编译器来尝试此操作。 在clang 4.0或gcc 7.1中,你可以传递--std=c++1z ,你的代码应该可以很好地编译。

当大括号存在时,您是复制列表 – 初始化返回值,不涉及复制/移动构造函数。 返回值是使用Position(const Obj&)构造Position(const Obj&) 就地构造的。

请注意,如果您将Position(const Obj&)构造函数explicit则即使使用大括号,代码也将无法编译,因为copy-list-initialization不允许显式构造函数被调用。

如果省略花括号,则语义上在该函数内构造一个临时的Position对象,并且返回值是从该临时对象构造的。 在实践中,大部分的实现都会使移动的构造变得迟钝,但是它仍然需要一个可行的移动构造函数来存在,在这里不是这样,因为它已经被明确地删除了。 这就是你的代码不能在没有大括号的情况下编译的原因。

使用C ++ 17编译器,由于保证了copy-elision,所以即使没有大括号也能编译代码。

这个不错! 这是因为return {...}意思是“返回函数的返回types的对象初始化与列表初始化…”。

列表初始化器在这里有更详细的描述:

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

所以,区别在于{*this}称之为:

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

尽pipe*this尝试通过使用显式删除的赋值运算符(前C + + 11,转换成Obj& Position ,他们将不得不被private ,如果列表初始化将可用,你会得到一个更令人混淆的错误消息…) :

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

坦率地说,使用你的类和下面的main():

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

它在两种情况下(-std = c ++ 14)都没有编译(gcc / mingw),有或没有大括号,它抱怨丢失的Position(Position &&)构造函数被删除。 这是合理的,因为似乎在这两种情况下都执行临时返回对象的构造,然后将其移动到目的地。 移动构造函数被删除是不可能的。 相反,使用-std = c ++ 17标记它在两种情况下编译(有或没有花括号),最有可能的是,我们正在对c ++ 17进行保证的返回值优化。 希望这可以帮助。