为什么允许T()= T()?

时间:2010-05-28 04:26:42

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

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

class T {};

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

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

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

编辑:我发现这个奇怪的原因是它在内置类型上被严格禁止,但允许在用户定义的类型上使用。例如,int(2) = int(3)将无法编译,因为这是“赋值中的无效左值”。

所以我想真正的问题是,这种有点不一致的行为是否构成了语言的原因?还是出于某种历史原因? (例如,在rvalue表达式上只允许调用const成员函数在概念上会更健全,但是这可能无法完成,因为这可能会破坏一些现有代码。)

5 个答案:

答案 0 :(得分:13)

这纯粹是因为运算符重载,以及你可能会重载operator =来做更多花哨的事情,比如打印到控制台,或锁定互斥锁,或其他任何东西。

答案 1 :(得分:7)

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

您可能还应该考虑到“作为rvalue”是表达式的属性,而不是对象的属性。确实T()表达式求值为右值。尽管如此,T()表达式生成的临时对象仍然是对象,也可以作为左值进行访问。例如,可以在赋值结果上调用其他一些成员函数,它将通过*this左值看到临时对象的“新”(新分配)值

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

您还可以通过将const引用附加到const T& r = T() = T();来延长临时的生命周期,并且通过r看到的值将是对象的“新”值。 / del>正如约翰内斯在评论中正确指出的那样,这不会将其附加到临时。

答案 2 :(得分:5)

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

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

答案 3 :(得分:4)

从一个POV来看,它是不一致的,但你忽略了它 的一致性:1)整数和其他内置类型仍然像在C中一样,2)operator = on class-types的行为与任何其他方法一样,不需要另外一个特殊情况。

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

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

鉴于我的druthers,事情会进一步简化,所以int() = int()将被允许。 C ++ 0x开始朝着rvalue-references,prvalues等方向前进。

答案 4 :(得分:4)

这就是为什么可以实现标准库中的几个类的原因。例如,考虑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[]返回的代理可以访问空间有效地打包成整数的位。