在以下示例中,gcc 7发出警告:
默认移动任务' B'调用一个非平凡的移动任务 虚拟基地的运营商' A' [-Wvirtual - 移动 - 分配]
如果我创建一个std::tuple<B>
对象。 Clang 5没有报告任何问题。如果从vector
移除Base
,问题就会消失。 Example
#include <tuple>
#include <vector>
class Base
{
public:
virtual ~Base();
std::vector<int> v;
};
class A : public Base
{
};
class B : public virtual A
{
};
int main()
{
B *b = new B;
B another{*b}; // <<<--- this compiles
std::tuple<B> t; // <<<--- gives warning
}
为什么会出现std::tuple
(并且没有移动分配),如果我需要保持这样的层次结构,那么解决它的正确方法是什么?
答案 0 :(得分:7)
警告与tuple
无关,由B
的移动分配触发。例如,以下代码生成警告
B b;
B t;
t = std::move(b);
gcc documentation解释了警告的意图
-Wno-virtual-move-assign
禁止使用非平凡的C ++ 11移动赋值运算符从虚拟基础继承警告。这是危险的,因为如果虚拟基地可以沿多个路径到达,则会多次移动,这可能意味着两个对象最终都处于移动状态。如果编写移动赋值运算符以避免从移动的对象移动,则可以禁用此警告。
这是一个example来演示问题
struct Base
{
Base() = default;
Base& operator=(Base&&)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
struct A : virtual Base {};
struct B : virtual Base {};
struct C : A, B {};
int main()
{
C c;
c = C();
}
这会产生输出
Base& Base::operator=(Base&&)
Base& Base::operator=(Base&&)
显示单个Base
实例已移动分配两次,每个移动分配操作符A
和B
移动一次。第二个移动分配来自已经从对象移动,这可能导致第一个移动分配的内容被覆盖。
请注意,clang也会在我的示例中生成警告,它可能会检测到示例中的两个路径无法访问A
,并且不会发出警告。
解决问题的方法是为A
和B
实施可以检测到Base
已移出的移动分配操作符,并省略第二个移动分配。