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

时间:2017-08-08 00:13:13

标签: c++ c++11

以下代码编译良好:

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;
}

4 个答案:

答案 0 :(得分:37)

当花括号出现时,你重新copy-list-initializing返回值,不涉及复制/移动构造函数。使用Position(const Obj&)构造函数的return value is constructed in-place

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

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

使用C ++ 17编译器,由于guaranteed copy-elision,您的代码即使没有大括号也会编译。

答案 1 :(得分:16)

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

在C ++ 11之前,当你想要o类型的默认构造和对象Obj并从Position p构造o时,你必须写< / 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 

第一行声明function,第二行尝试使用以函数指针作为参数的构造函数创建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 - 语句,这导致了您的问题的答案:return {*this};return *this;之间的区别在于前者初始化返回值<来自*this的强>直接,而后者首先将*this转换为临时Position对象,然后从此临时间接初始化返回值,因为复制和移动构造函数都已被明确删除而失败。

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

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

答案 2 :(得分:13)

这是一个很好的!这是因为return {...}表示“返回使用列表初始值设定项初始化的函数返回类型的对象...”。

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

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

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

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

*this尝试使用明确删除的赋值运算符将Obj&转换为Position,而在C ++ 11之前,必须将它们设为private,并且你'如果列表初始化程序可用,则会收到更令人困惑的错误消息...):

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

答案 3 :(得分:-2)

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

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


    cout.flush();
    return 0;
}

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