Consider the following program:
#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) and clang++ both print different values for C1 and C2 whereas Visual Studio 2013 and 2015 consistently print the lower case alphabet sequence for both C1 and C2.
Who is right? or is it just some loop hole in the standard?
From §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 forC
. — end example ]
The behavior of your program is unspecified according to the standard. Visual Studio decides to call the assignment operator once while GCC and Clang do so twice. The result of a move operation leaves the move-from object in a valid but unspecified state, so subsequently moving from the temporary again will leave s
with an unspecified value, but per the above clause this is allowed to happen. So to answer your question, there is no correct behavior for this program.