【问题标题】:Why is T() = T() allowed?为什么允许 T() = T()?
【发布时间】:2011-02-24 23:31:12
【问题描述】:

我相信表达式 T() 创建了一个右值(按照标准)。但是,以下代码可以编译(至少在 gcc4.0 上):

class T {};

int main()
{
    T() = T();
}

我知道从技术上讲这是可能的,因为可以在临时对象上调用成员函数,而上面只是在从第一个 T() 创建的右值临时对象上调用 operator=。

但从概念上讲,这就像为右值分配一个新值。是否有充分的理由允许这样做?

编辑: 我觉得这很奇怪的原因是它在内置类型上是严格禁止的,而在用户定义类型上却是允许的。例如,int(2) = int(3) 不会编译,因为这是“赋值中的无效左值”。

所以我想真正的问题是,这种在语言中内置的有些不一致的行为是有原因的吗?还是出于某种历史原因? (例如,只允许在右值表达式上调用 const 成员函数在概念上会更合理,但不能这样做,因为这可能会破坏一些现有代码。)

【问题讨论】:

    标签: c++ operator-overloading language-design rvalue temporary-objects


    【解决方案1】:

    之所以允许这样做,纯粹是因为运算符重载,并且您可能会重载 operator = 以执行更花哨的操作,例如打印到控制台、锁定互斥体或其他任何事情。

    【讨论】:

    • 不仅是 operator=,还有构造函数。 C++ 中有很多奇怪的结构,编译器只是耸耸肩,希望你知道自己在做什么。 :-)
    【解决方案2】:

    是的,您正在为右值分配一个新值。更准确地说,您是在右值上调用 operator = 成员函数。既然您没有使用内置的赋值运算符,为什么您认为这应该是一个问题? operator = 是类的成员函数,在大多数方面与类的任何其他成员函数相似,包括它可以在右值上调用。

    您可能还应该考虑“作为右值”是表达式的属性,而不是对象的属性这一事实。确实,T() 表达式的计算结果为右值。尽管如此,T() 表达式生成的临时对象仍然是一个 object,它也可以作为左值访问。比如可以在赋值的结果上调用其他一些成员函数,它会通过*thislvalue看到临时对象的“新”(新赋值)值

    (T() = T()).some_member_function();
    

    您还可以通过将常量引用附加到它const T& r = T() = T(); 来延长临时对象的生命周期,通过r 看到的值将是对象的“新”值。 Johannes 在他的评论中正确指出,这不会将其附加到临时的。

    【讨论】:

    • 是的,我想我了解此类操作“如何”工作的机制。但我仍然很好奇“为什么”语言设计者允许 r 值(或从 r 值表达式创建的临时变量)如此突变?这是他们的疏忽还是故意允许的(也许有一些实际原因足以证明内置类型和用户定义类型之间看似不一致的行为是合理的?)
    • 澄清一下,如果只允许在右值表达式上调用 const 成员函数,我个人会觉得不那么令人惊讶。
    • @Rhimo:这打败了像Atomic(std::cout) << 1 << " uninterrupted output" << std::endl;这样的合理结构
    • 这里的小疏忽,我认为:const T& r = T() = T(); 行不通。您正在尝试将 const 引用绑定到左值(复制赋值运算符返回的T&),然后临时值将被销毁。仅当您尝试将其绑定到引用临时值的右值时,生命周期才会延长。
    【解决方案3】:

    从一个 POV 来看,它是不一致的,但您忽略了它是如何保持一致的:1) int 和其他内置类型的行为仍与 C 中的行为相同,2) operator= on class-types 的行为与任何其他方法一样,无需另一种特殊情况。

    自 C++ 开始以来,C 兼容性就受到高度重视,如果没有它,C++ 可能不会在今天出现。所以这部分通常是一件好事。

    第二点被低估了。不特殊的大小写 operator= 允许无意义的代码“工作”,但为什么我们首先关心无意义的代码呢?垃圾进垃圾出。就我所见,目前的规则赋予了它一个明确的含义(UB 在这里会很糟糕),而成本可以忽略不计。

    考虑到我的想法,事情会进一步简化,所以int() = int() 将被允许。 C++0x 开始朝着这个方向发展,包括右值引用、纯右值等。

    【讨论】:

    • "C++0x 开始朝着这个方向发展,包括右值引用、右值等。"拥抱?
    【解决方案4】:

    您可以将 operator= 限制为仅对 C++0x 中的左值起作用:

    class T
    {
    public:
        T& operator=(const T&) & = default;
    };
    

    【讨论】:

      【解决方案5】:

      这就是标准库中可以实现多个类的原因。例如考虑std::bitset<>::operator[]

      // bit reference:
      class reference {
        friend class bitset;
        reference();
      public:
        ˜reference();
        reference& operator=(bool x);           // for b[i] = x;
        reference& operator=(const reference&); // for b[i] = b[j];
        bool operator˜() const; // flips the bit
        operator bool() const;  // for x = b[i];
        reference& flip();      // for b[i].flip();
      };
      
      reference operator[](size_t pos); // for b[i];
      

      如果您执行bits[i] = true,您会为类类型的右值准确地分配一些值。 operator[] 返回的代理可以访问空间有效地打包成整数的位。

      【讨论】:

      • 这与 FredOverflow 的回答一起完全回答了我的问题。谢谢!
      猜你喜欢
      • 2017-03-19
      • 2014-12-07
      • 2010-11-01
      • 1970-01-01
      • 2012-03-07
      • 2012-02-03
      • 1970-01-01
      相关资源
      最近更新 更多