花括号内的c ++ {*this}

ar2*_*015 50 c++ c++11

以下代码编译正常:

g++ -std=c++11 test.cpp -Wall -Wextra -Wfatal-errors && ./a.out
Run Code Online (Sandbox Code Playgroud)

但是,如果我从中移除花括号{*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;
}
Run Code Online (Sandbox Code Playgroud)

Pra*_*ian 37

当花括号出现时,你是copy-list-initialize返回值,不涉及复制/移动构造函数.该返回值被就地建造使用的Position(const Obj&)构造函数.

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

如果省略花括号,则在语义上Position在函数内构造临时对象,并从该临时值构造返回值.实际上,大多数实现都会忽略移动构造,但它仍然需要一个可行的移动构造函数才能存在,而这种情况并非如此,因为它已被明确删除.这就是你的代码在没有大括号的情况下无法编译的原因.

使用C++ 17编译器,即使没有大括号,您的代码也会编译,因为保证了copy-elision.


Tob*_*ias 17

两者之间的区别非常微妙.C++ 11引入了功能列表初始化(有时也称为大括号初始化):

在C++ 11之前,当你想要默认构造和o类型的对象Obj并构造一个Position pfrom时o,你必须写

Obj o;              // default construct o
Obj::Position p(o); // construct p using Position(Obj const&)
Run Code Online (Sandbox Code Playgroud)

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

Obj o();            // mistake: declares a function o returning an Obj
Obj::Position p(o); // error: no constructor takes a function 
Run Code Online (Sandbox Code Playgroud)

第一行声明一个函数,第二行尝试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&)
Run Code Online (Sandbox Code Playgroud)

这种新的语法也适用于return-statements,这导致你的问题的回答:之间差异return {*this};以及return *this;在于,前者初始化返回值直接*this,而后者首先将*this一个临时Position对象,然后间接地初始化返回值来自这个临时的,因为复制和移动构造函数都已被明确删除而失败.

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

您可以使用支持C++ 17的编译器来尝试此操作.在clang 4.0或gcc 7.1中,您可以传递--std=c++1z,并且您的代码应该使用和不使用大括号进行编译.


isp*_*zax 13

这个不错!这是因为return {...}意味着"返回使用列表初始值设定项初始化的函数返回类型的对象...".

列表初始值设定项在此处有更详细的描述:

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

所以,区别在于{*this}调用:

inline Position(const Obj& ref): ref(ref){}
Run Code Online (Sandbox Code Playgroud)

*this试图转换Obj&Position使用显式删除赋值运算符(预C++ 11,他们将不得不作出private的,你会得到一个更令人困惑的错误消息,如果列表中的初始化将可...):

inline Position(Position const &) = delete;
inline Position(Position &&) = delete;
Run Code Online (Sandbox Code Playgroud)