MSVC++为左值而不是右值使用基运算符=重载

MSVC++ uses base operator = overload for lvalue rather than rvalue?

本文关键字:运算符 重载 MSVC++      更新时间:2023-10-16

这是编译器启动屏幕(针对版本等(:

C:Program FilesMicrosoft Visual Studio 10.0VC>cl.exe
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
usage: cl [ option... ] filename... [ /link linkoption... ]

我有一个基类(它是一个模板(,想象一下它是:

template <typename T>
class Base {
    public:
        Base<T> & operator = (const Base<T> &);
        Base<T> & operator = (Base<T> &&);
};

然后我有一个派生类,它不会以任何方式重新实现operator =

如果我执行以下操作:

Derived<int> derived;
derived=Derived<int>();

在第二行上调用接受左值的CCD_ 2。

如果我进入Derived<T>的定义并添加以下内容:

template <typename T>
Derived<T> & Derived<T>::operator = (Derived<T> && other) {
    Base<T>::operator=(static_cast<Base<T> &&>(other));
    return *this;
}

接受右值的operator =被调用。

即使我也实现了采用左值的operator =(以大致相同的方式(,这种行为仍然存在。

因为没有更好的短语:什么给予?

我是误解了C++,还是它不应该这样工作?

摘要:您只需要手动定义所需的移动分配运算符,直到VS更新为具有更完整的C++11支持为止。


由于您没有声明复制或移动赋值运算符,编译器可能会隐式地为您声明和定义它们。然后,这些默认实现使用基类复制和移动赋值运算符来复制或移动基类子对象。

但是,在许多情况下,移动赋值运算符的隐式声明都会被抑制,例如,如果类具有用户声明的复制ctor、移动ctor、复制赋值运算符或dtor。如果你的课上有这些,那么你不会自动得到移动作业操作符[编辑:Alexandre C.指出,VS2010无论如何都不会隐式声明移动赋值运算符。]

如果没有移动分配运算符,derived=Derived<int>();将调用隐式声明/定义的复制分配运算符,后者将调用Base<T>的复制分配操作符,而不调用Base<T>::operator = (Base<T> &&)

如果您想要移动赋值运算符的默认定义,即使您还需要做一些抑制其隐式声明的事情,您也可以使用新的= default语法(尽管标准中存在缺陷,这意味着在某些情况下= default实际上不会为您提供默认定义,但它已经得到解决(,但是VS10不支持= default。您只需要手动定义所需的移动分配运算符,直到VS更新为具有更完整的C++11支持为止。

我认为,在完全支持C++11的情况下,最好不要依赖隐式声明,并且那些可以隐式声明的特殊成员函数应该始终显式声明(如果您不希望生成= delete(。隐式声明最初是为了与C向后兼容。现在C++11添加了= default,显式声明更容易理解,也没有缺点。

MSVC从不生成隐式移动构造/赋值。看见http://msdn.microsoft.com/en-us/library/dd293668.aspx。

原因是隐含移动语义在标准细化过程中发生了许多变化,而在MSVC10完成时,最终的共识还没有达成。

最重要的是:您必须显式地声明您想要手动使用的每个移动构造函数/移动赋值运算符。没有= default修饰符可以帮助您。这意味着要编写大量swap成员函数,或者干脆放弃移动语义,除非您1(确实需要它们(不可复制的类(,或者2(已经对代码进行了分析并需要消除副本。

默认的副本分配是隐式生成的,这就是您在这里得到的。

Standart(3337,12.8.24(.Standart
如果我们使用Base::operator=;Base::将使用运算符=(Base&&(。

在这种情况下,只要Derived<T>没有任何用户声明的复制操作、移动操作或用户声明的析构函数,就应该调用Base<int>的移动构造函数。Derived<T>有一个隐式声明的移动赋值运算符,它将移动基类子对象,包括其Base<T>基类子对象。

但是,Visual C++(自Visual C++2012 RC起(不会隐式生成移动操作,因此您可以看到执行的是复制而不是移动。如果要聚合或派生可移动类型,并且希望聚合或派生类型是可移动的,则必须定义自己的移动构造函数和移动赋值运算符。

在过去两年的标准化过程中,移动操作的规范及其隐含声明的情况发生了多次变化。直到2010年2月,移动操作的隐式声明才被添加到C++11中(即,在Visual C++2010完成后,它发生了变化(。在去年C++11完成之前,规范发生了几次更改,这些更改引起了相当大的争议。

Visual C++2012也不会隐式生成移动操作。