当虚拟基地移动分配操作符现在被允许使用时,它们会带来危险

Danger with virtual base move assignment operators when they are now allowed to be used?

本文关键字:许使用 危险 虚拟 移动 分配 操作符      更新时间:2023-10-16

这涉及C++问题的解决http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1402。摘要:

template<typename T>
struct wrap
{
   wrap() = default;
   wrap(wrap&&) = default;
   wrap(const wrap&) = default;
   T t;
};
struct S {
   S(){}
   S(const S&){}
   S(S&&){}
};
typedef wrap<const S> W;
// Error, defaulted move constructor of "wrap<const S>" is deleted!
W get() { return W(); }

(问题是,尽管编译器可以简单地使用"S"的复制构造函数,就像用户显式写入"wrap"的移动构造函数时一样,但我们在这个代码段中遇到了一个错误。解决了这个问题,在重载解决过程中忽略了已删除的移动构造函数(和赋值运算符),因此根据需要使用了上面的复制构造函数。)

在起草该决议时,对该决议发表了以下评论:

相对于复制,隐式/默认移动操作没有其他限制。这意味着在虚拟库中进行移动分配是危险的(编译器可能应该发出警告)[…]但我们在巴达维亚决定,我们不会针对隐式移动操作保留所有C++03代码,我认为此解决方案提供了显著的性能优势。

有人能描述一下虚拟基地移动分配操作员的担忧吗?

考虑:

#include <iostream>
struct A
{
    A() = default;
    A(const A&) = default;
    A(A&&) = default;
    A& operator=(const A&) {std::cout << "operator=(const A&)n"; return *this;}
    A& operator=(A&&) {std::cout << "operator=(A&&)n"; return *this;}
};
struct B
    : virtual public A
{
};
struct C
    : virtual public A
{
};
struct D
    : public B,
      public C
{
};
int
main()
{
    D d1, d2;
    d1 = std::move(d2);
}

我认为这个程序应该输出:

operator=(A&&)
operator=(A&&)

对我来说,它实际上输出:

operator=(const A&)
operator=(const A&)

但我认为这只是一个clang bug(用-std=c++1y编译)。如果我对输出应该是什么是正确的,那么危险在于移动分配操作符被调用两次。这对于复制分配操作符是无害的(尽管可能很昂贵),但对于移动分配操作符则不是。