【问题标题】:Move semantics in the presence of virtual bases在存在虚拟基础的情况下移动语义
【发布时间】:2015-05-12 14:48:57
【问题描述】:

考虑以下程序:

#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 打印小写字母序列。

谁是对的?还是只是标准中的一些漏洞?

【问题讨论】:

  • 来自 clang 的有用警告:C 的默认移动赋值运算符将多次移动分配虚拟基类 Name
  • @0x499602D2 我的问题仍然存在 :-) 谁是对的?还是没有正确答案?
  • 看起来像 UB。 c1 = f() 两次从f() 产生的临时移动。
  • @0x499602D2 至少在语义上,我看到了 VS 2013/2015 的“正确”输出。我猜是因为它的 UB,我看到什么并不重要,因为没有真正的答案?

标签: c++11 move-semantics virtual-inheritance diamond-problem


【解决方案1】:

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

未指定表示虚拟基类的子对象是否由 隐式定义的复制/移动赋值运算符。 [ 例子:

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

未指定虚拟基类子对象V 是否由C 的隐式定义的复制/移动赋值运算符分配两次。 —结束示例]

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

【讨论】:

  • 不是“不确定值”。
  • @T.C.还应该是“未指定”吗?
  • 是的,第一个移动分配使s 处于有效但未指定的状态;从具有有效但未指定状态的事物中移动分配使目的地也具有有效但未指定状态。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 2012-10-29
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
  • 2013-04-02
  • 2012-05-19
相关资源
最近更新 更多