警告:X的默认移动分配运算符将多次移动分配虚拟基类Y [英] Warning: defaulted move assignment operator of X will move assign virtual base class Y multiple times

查看:76
本文介绍了警告:X的默认移动分配运算符将多次移动分配虚拟基类Y的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++ 11下测试库时,我在Clang下收到警告.我以前从未遇到过警告,并且搜索并没有提供太多的阅读和研究方式.

I'm catching a warning under Clang when testing a library under C++11. I've never come across the warning before and searching is not providing too much in the way of reading and research.

警告如下所示,它似乎与多重继承和公共基类有关.但是我不清楚触发警告的细节或应对措施.

The warning is shown below, and it appears to be related to multiple inheritance and a common base class. But I'm not clear on the details triggering the warning or what I should do to address it.

我的第一个问题是这是需要解决的问题吗?还是这仅仅是效率问题吗?

My first question is, Is this a problem that needs to be addressed? Or is it a matter of efficiency alone?

我的第二个问题是(如有需要),我该如何解决该警告?有哪些可用的补救措施?

My second question is (if needed), How do I address the warning? Or what are the options available to remediate it?

以下是一些其他信息:

  • 编译器:Apple LLVM 6.0版(clang-600.0.57)(基于LLVM 3.5svn)
  • g ++ -DDEBUG -g2 -O2 -std = c ++ 11 -fPIC -march = native -pipe -c test.cpp

我还在Stack Overflow上回顾了以下内容,但我不清楚它们相交的位置:

Is also reviewed the following on Stack Overflow, but its not clear to me where they intersect:

Crypto ++ 库也大量使用了 标头文件可在线获得,这是实际的警告:

The the header file is available online, and here is the actual warning:

g++ -DDEBUG -g2 -O2 -std=c++11  -Wno-deprecated-declarations -fPIC -march=native -pipe -c rsa.cpp
In file included from rsa.cpp:4:
In file included from ./rsa.h:12:
./pubkey.h:635:26: warning: defaulted move assignment operator of 'InvertibleRSAFunction' will move assign virtual base class 'CryptoMaterial' multiple times [-Wmultiple-move-vbase]
class CRYPTOPP_NO_VTABLE TF_ObjectImpl : public TF_ObjectImplBase<BASE, SCHEME_OPTIONS, KEY_CLASS>
                         ^
./rsa.h:57:44: note: 'CryptoMaterial' is a virtual base class of base class 'CryptoPP::RSAFunction' declared here
class CRYPTOPP_DLL InvertibleRSAFunction : public RSAFunction, public TrapdoorFunctionInverse, public PKCS8PrivateKey
                                           ^~~~~~~~~~~~~~~~~~
./rsa.h:57:96: note: 'CryptoMaterial' is a virtual base class of base class 'CryptoPP::PKCS8PrivateKey' declared here
class CRYPTOPP_DLL InvertibleRSAFunction : public RSAFunction, public TrapdoorFunctionInverse, public PKCS8PrivateKey
                                                                                               ^
1 warning generated.

我很抱歉没有减少它.我不确定如何减少警告和投诉的本质.

My apologies for not reducing it. I'm not sure how to reduce it and capture the essence of the warning/complaint.

推荐答案

警告对我来说似乎是不言自明的,它告诉您,对派生类型进行移动分配会导致对基础进行两次移动分配.

The warning seems self-explanatory to me, it's telling you that move-assigning the derived type will result in move-assigning the base twice.

减少它是微不足道的,只需使用虚拟基础和两个到达它的路径来创建继承层次结构即可:

Reducing it is trivial, just create an inheritance hierarchy using a virtual base and two paths to get to it:

#include <stdio.h>

struct V {
    V& operator=(V&&) { puts("moved"); return *this; }
};

struct A : virtual V { };

struct B : virtual V { };

struct C : A, B { };

int main() {
    C c;
    c = C{};
}

这将打印两次"moved",因为ABC的每个隐式移动分配运算符都会进行成员分配,这意味着A::operator=(A&&)B::operator=(B&&)都将进行分配基类.正如Alan所说,这是该标准的有效实施. (该标准规定,在构造时,只有最派生的类型才能构造虚拟库,但是对分配的要求不相同.)

This will print "moved" twice, because the implicit move assignment operators for each of A, B and C will do a memberwise assignment, which means that both A::operator=(A&&) and B::operator=(B&&) will assign the base class. As Alan says, this is a valid implementation of the standard. (The standard specifies that on construction only the most-derived type will construct the virtual base, but it doesn't have the same requirement for assignment).

这不是特定于移动分配的,将基类更改为仅支持副本分配而不移动分配将打印"copied"两次:

This isn't specific to move assignment, changing the base class to only support copy assignment and not move assignment will print "copied" twice:

struct V {
    V& operator=(const V&) { puts("copied"); return *this; }
};

发生这种情况的原因完全相同,A::operator=(A&&)B::operator=(B&&)都将分配基类.编译器不会针对这种情况发出警告,因为(可能)两次执行副本分配只是次优的,这不是错误的.对于移动分配,可能会丢失数据.

This happens for exactly the same reason, both of A::operator=(A&&) and B::operator=(B&&) will assign the base class. The compiler doesn't warn for this case, because doing copy assignment twice is (probably) just suboptimal, not wrong. For move-assignment it might lose data.

如果您的虚拟基础实际上没有任何需要复制或移动的数据,或者仅具有可微复制的数据成员,那么使其仅支持复制不移动将抑制该警告:

If your virtual base doesn't actually have any data that needs to be copied or moved, or only has data members that are trivially copyable, then making it only support copying not moving will suppress the warning:

struct V {
    V& operator=(const V&) = default;
};

此复制赋值运算符仍将被调用两次,但是由于它不执行任何操作,因此没有问题.不做两次仍然没有任何反应.

This copy assignment operator will still get called twice, but since it doesn't do anything there's no problem. Doing nothing twice is still nothing.

(这里的GCC似乎比Clang聪明一点,它没有警告过虚拟基座的移动赋值运算符如果很琐碎就被调用两次,因为琐碎的移动等效于一个副本,因此不太可能成为问题).

(GCC seems a bit smarter than Clang here, it doesn't warn about the virtual base's move assignment operator being called twice if it's trivial, because a trivial move is equivalent to a copy and so is less likely to be a problem).

如果虚拟库确实有需要在分配时复制的数据,则使其进行复制而不进行移动可能仍然是一个不错的选择,但这取决于类型和作用.您可能需要在层次结构的每个级别上明确定义复制和移动分配.虚拟基础非常棘手,难以正确使用,尤其是面对复制或移动时.将具有虚拟基数的类型视为可以轻松复制和移动的值类型可能是设计错误.

If the virtual base does have data that needs to be copied on assignment then making it do a copy not a move might still be a good choice, but it depends what the type is and does. You might need to define copy and move assignment explicitly at every level of the hierarchy. Virtual bases are tricky and hard to use correctly, especially in the face of copying or moving. Treating types with virtual bases as value types that can be copied and moved easily might be a design error.

iostreams层次结构使用虚拟基础,但是要仔细且正确地完成. iostream类型是不可复制的,只能移动,而派生类型明确定义了移动分配,以确保basic_ios<>基类仅更新一次.具体来说,basic_iostream::operator=(basic_iostream&&)仅在basic_istream基础上运行,而不在basic_ostream基础上运行.上面的示例等效于:

The iostreams hierarchy uses virtual bases, but is done carefully and correctly. The iostream types are non-copyable, only movable, and the derived types define move assignment explicitly to ensure the basic_ios<> base class only gets updated once. Specifically, basic_iostream::operator=(basic_iostream&&) only operates on the basic_istream base, not the basic_ostream one. The equivalent for the example above would be:

struct C : A, B {
     C& operator=(C&& c) {
         static_cast<A&>(*this) = static_cast<A&&>(c);
         return *this;
     }
};

直到r ++引用和移动语义使得使用有用的语义成为可能,直到C ++ 11,Iostream才完全不可复制.如果您的类在C ++ 03中始终是可复制的,那么它可能已经是一个有问题的设计,应该是不可复制的,或者精心编写的复制操作不是隐式定义的.

Iostreams were not copyable at all until C++11 when rvalue references and move semantics made it possible to do with useful semantics. If your class has always been copyable in C++03 it might already have been a questionable design that should have had been non-copyable, or have carefully written copy operations not implicitly-defined ones.

简而言之,只要您拥有虚拟基础,就需要仔细考虑构造,赋值和销毁的工作方式,以及复制和赋值对这种类型是否有意义.

In short, any time you have virtual bases you need to think very carefully about how construction, assignment and destruction work, and whether copying and assignment even make sense for the type.

这篇关于警告:X的默认移动分配运算符将多次移动分配虚拟基类Y的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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