【问题标题】:c++ {*this} inside curly bracesc++ {*this} 在花括号内
【发布时间】:2017-08-08 00:13:13
【问题描述】:

以下代码编译良好:

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++ c++11


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我想知道为什么我在错误中看到Position(Obj::Position&amp;&amp;)’ 而不是Position(Obj::Position&amp;)’ 当我省略大括号时。看起来正好相反。
    • @ar2015 您的意思是为什么您在错误消息中看到Position(Position&amp;&amp;) 而不是Position(Position const&amp;)?这是因为您有一个prvalue Position 对象来初始化返回值,并且移动构造函数是更好的匹配。如果注释掉inline Position(Position &amp;&amp;) = delete; 行,错误消息将包含复制构造函数。
    • 它基本上是“我禁止它,然后我尝试这样做”如果确实需要的话
    【解决方案2】:

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

    在 C++11 之前,当你想要默认构造和对象 Obj 类型的 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 
    

    第一行声明了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-statements,这就引出了你的问题的答案:return {*this};return *this;的区别在于前者初始化返回值直接 来自*this,而后者首先将*this 转换为临时Position 对象,然后从这个临时对象间接初始化返回值,因为复制和移动构造函数都失败了已被明确删除。

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

    您可以使用支持 C++17 的编译器进行尝试。在 clang 4.0 或 gcc 7.1 中,您可以传递 --std=c++1z,并且您的代码无论有无大括号都应该可以正常编译。

    【讨论】:

      【解决方案3】:

      这个不错!这是因为return {...} 的意思是“返回一个使用列表初始化器初始化的函数返回类型的对象......”。

      这里更详细地描述了列表初始值设定项:

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

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

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

      *this 尝试使用显式删除的赋值运算符将Obj&amp; 转换为Position(在C++11 之前,必须将它们设置为private,并且您会收到更令人困惑的错误消息如果列表初始值设定项可用...):

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

      【讨论】:

        【解决方案4】:

        坦率地说,使用您的课程和以下 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 的保证返回值优化。 希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 2019-01-26
          • 2018-05-02
          • 2014-02-16
          • 2019-11-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多