为什么gcc警告有关使用std :: tuple和虚拟继承调用非平凡的移动赋值运算符? [英] Why does gcc warn about calling a non-trivial move assignment operator with std::tuple and virtual inheritance?

查看:108
本文介绍了为什么gcc警告有关使用std :: tuple和虚拟继承调用非平凡的移动赋值运算符?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下示例中,gcc 7给出了警告:

In the following example gcc 7 gives a warning:

"B"的默认移动分配称为非平移分配虚拟基地"A"的运营商[-Wvirtual-move-assign]

defaulted move assignment for 'B' calls a non-trivial move assignment operator for virtual base 'A' [-Wvirtual-move-assign]

如果我创建一个 std :: tuple< B> 对象.Clang 5没有报告任何问题.如果从 Base 中删除了 vector ,问题也就消失了.示例.

if I create an std::tuple<B> object. Clang 5 doesn't report any problems. Also the problem goes away if vector is removed from 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 (并且没有移动分配)的情况下会发生这种情况,并且如果我需要保持这样的层次结构,那么修复它的正确方法是什么?

Why does it happen in presence of std::tuple (and absence of move assignment) and what is the proper way to fix it if I need to keep such a hierarchy?

推荐答案

该警告与 tuple 无关,它是由 B 的移动分配触发的.例如,以下代码会生成警告

The warning is unrelated to tuple, it is triggered by a move assignment of B. For instance, the following code generates the warning

B b;
B t;
t = std::move(b);

gcc文档解释了警告的意图

-Wno-virtual-move-assign

禁止有关使用非平凡的C ++ 11移动分配运算符从虚拟库继承的警告.这很危险,因为如果虚拟基础可以沿着一条以上的路径到达,则虚拟基础会被移动多次,这可能意味着两个对象最终都处于移出"状态.如果编写了移动分配运算符以避免从已移动的对象移动,则可以禁用此警告.

Suppress warnings about inheriting from a virtual base with a non-trivial C++11 move assignment operator. This is dangerous because if the virtual base is reachable along more than one path, it is moved multiple times, which can mean both objects end up in the moved-from state. If the move assignment operator is written to avoid moving from a moved-from object, this warning can be disabled.

这是一个示例,该问题可用于演示

Here's an example that demonstrates the problem

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 的移动赋值运算符执行.第二个移动分配来自已移动的对象,这可能导致第一个移动分配中的内容被覆盖.

showing that the single Base instance was moved assigned to twice, once by each move assignment operator of A and B. The second move assignment is from an already moved from object, which could cause the contents from the first move assignment to be overwritten.

请注意,在我的示例中clang还会生成警告,它可能检测到示例中的两条路径无法到达 A 且未发出警告.

Note that clang also generates a warning in my example, it's probably detecting that A is not reachable via two paths in your example and not emitting the warning.

解决此问题的方法是为 A B 实现移动分配运算符,这些运算符可以检测到 Base 已从中移出并忽略了第二招.

The way to fix it is to implement move assignment operators for A and B that can detect that Base has been moved from and omit a second move assignment.

这篇关于为什么gcc警告有关使用std :: tuple和虚拟继承调用非平凡的移动赋值运算符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆