在存在虚拟基础的情况下移动语义

Move semantics in the presence of virtual bases

考虑以下程序:

#include <iostream>
#include <ostream>
#include <string>
#include <utility>

using namespace std;

struct Name { string s; Name(string s) : s(move(s)) { } };

struct A : virtual Name { A(string s) : Name(move(s)) { } };

struct B : virtual Name { B(string s) : Name(move(s)) { } };

struct C : A, B { C(string s) : A(string()), B(string()), Name(move(s)) { } };

C f() { return C("abcdefghijklmnopqrstuvwxyz"); }

int main()
{
    C c1("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    C c2("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    C ff = f();
    c1 = f();
    c2 = ff;
    cout << "C1 = " << c1.s << " " << "C2 = " << c2.s << "\n";
    return 0;
}

gcc (4.9.2) (http://ideone.com/G7uzCQ) 和 clang++ 都为 C1 和 C2 打印不同的值,而 Visual Studio 2013 和 2015 始终为 C1 和 C2 打印小写字母序列。

谁是对的?或者它只是标准中的一些漏洞?

来自 §12.8/28 [class.copy]:

It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy/move assignment operator. [ Example:

struct V { };
struct A : virtual V { };
struct B : virtual V { };
struct C : B, A { };

It is unspecified whether the virtual base class subobject V is assigned twice by the implicitly-defined copy-/move assignment operator for C. — end example ]

根据标准,您的程序的行为未指定。 Visual Studio 决定调用赋值运算符一次,而 GCC 和 Clang 调用两次。移动操作的结果使移出对象处于有效但未指定的状态,因此随后再次从临时对象移动将留下 s 具有未指定的值,但根据上述条款,这是允许发生的。所以要回答你的问题,这个程序没有正确的行为。