MSVC ++使用base operator = overload来代替左值而不是rvalue?

时间:2012-07-04 19:36:14

标签: c++ visual-studio-2010 visual-c++ rvalue-reference lvalue

这是编译器启动画面(版本等):

C:\Program Files\Microsoft Visual Studio 10.0\VC>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>();

在第二行调用接受左值的operator =

如果我进入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 ++,或者这不是它应该如何运作的?

4 个答案:

答案 0 :(得分:2)

总结:您只需手动定义所需的移动赋值运算符,直到使用更完整的C ++ 11支持更新VS.


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

但是,在许多情况下,移动赋值运算符的隐式声明被抑制,例如,如果类具有用户声明的copy-ctor,move-ctor,copy-assignment运算符或dtor。如果您的班级中有任何一个,那么您不会自动获得移动赋值运算符。 [编辑:并且Alexandre C.指出VS2010永远不会隐含地声明移动分配运算符或ctors。]

如果没有移动赋值运算符derived=Derived<int>();,则调用隐式声明/定义的复制赋值运算符,该运算符调用Base<T>的复制赋值运算符,并且不调用Base<T>::operator = (Base<T> &&)

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

我认为在完全支持C ++ 11的情况下,最好不要依赖隐式声明,而且可以隐式声明那些可以隐式声明的特殊成员函数(如果不是= delete想要一个生成)。隐式声明最初是为了与C向后兼容而完成的。显式声明更容易理解,并且在C ++ 11添加= default时没有任何缺点。

答案 1 :(得分:1)

标准(3337,12.8.24)。 standart
如果我们使用Base :: operator =;将使用Base :: operator =(Base&amp;&amp;)。

答案 2 :(得分:1)

MSVC永远不会生成隐式移动构建/分配。见http://msdn.microsoft.com/en-us/library/dd293668.aspx

原因是隐式移动语义在标准阐述过程中经历了许多变化,并且在MSVC10完成时最终的共识并不存在。

底线:您必须明确声明您想要手动使用的每个移动构造函数/移动赋值运算符。没有= default修饰符可以帮助您。这意味着编写了很多swap成员函数,或者只是放弃了移动语义,除非你1)确实需要它们(不可复制的类)或2)已经分析了你的代码并且需要删除副本。

默认复制分配是隐式生成的,这就是你在这里得到的。

答案 3 :(得分:1)

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

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

在标准化的最后两年中,移动操作的规范以及隐式声明它们的情况多次改变。直到2010年2月,移动操作的隐式声明才被添加到C ++ 11中(即,它在Visual C ++ 2010完成后发生了变化)。之后,在去年完成C ++ 11之前,规范发生了几次变化,这些变化颇具争议。

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