1

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

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 =which 采用左值(以几乎相同的方式),这种行为仍然存在。

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

我是否误解了 C++ 或者这不是它应该如何工作的?

4

4 回答 4

2

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


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

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

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

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

我认为有了完整的 C++11 支持,最好不要依赖隐式声明,并且那些可以隐式声明的特殊成员函数应该始终显式声明(就= delete好像你不想生成一个一样)。隐式声明最初是为了与 C 向后兼容而完成的。显式声明更容易理解,并且由于 C++11 添加了= default.

于 2012-07-04T20:29:43.350 回答
1

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

于 2012-07-04T19:52:04.883 回答
1

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

原因是隐式移动语义在标准制定过程中发生了很多变化,最终共识在 MSVC10 完成时还没有出现。

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

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

于 2012-07-04T20:52:34.720 回答
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 也不会隐式生成移动操作。

于 2012-07-04T20:59:21.600 回答