[英]Warning: defaulted move assignment operator of X will move assign virtual base class Y multiple times
在C ++ 11下测试库时,我正在Clang下发出警告。 我以前从来没有遇到过警告,搜索并没有提供太多的阅读和研究。
警告如下所示,它似乎与多重继承和公共基类有关。 但我不清楚触发警告的细节或我应该做些什么来解决它。
我的第一个问题是, 这是一个需要解决的问题吗? 或者仅仅是效率问题?
我的第二个问题是(如果需要), 我该如何处理警告? 或者有哪些可用于修复它的选项?
以下是一些其他信息:
还回顾了Stack Overflow上的以下内容,但我不清楚它们在哪里交叉:
库Crypto ++也大量使用Curiously Recurring Template Pattern进行编译时多态性。
头文件可在线获取,这是实际警告:
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.
我为不减少它而道歉。 我不确定如何减少它并捕捉警告/投诉的本质。
该标准允许实现选择一种简单但有时破坏的方式来处理存在虚拟基础的成员分配。
http://en.cppreference.com/w/cpp/language/move_assignment :
与复制赋值一样,未指定是否可通过隐式定义的移动赋值运算符多次分配可通过继承点阵中的多个路径访问的虚拟基类子对象。
这对于移动分配来说尤其令人讨厌,因为它可能意味着从已经移动的成员分配。
警告对我来说似乎是不言自明的,它告诉你移动 - 分配派生类型将导致移动 - 分配基数两次。
减少它是微不足道的,只需使用虚拟基础和两个路径来创建一个继承层次结构:
#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"
两次,因为A
, B
和C
每A
的隐式移动赋值运算符将执行成员赋值,这意味着A::operator=(A&&)
和B::operator=(B&&)
都将分配基类。 正如艾伦所说,这是该标准的有效实施。 (该标准规定,在构造时,只有最派生的类型才会构造虚拟基础,但它对分配没有相同的要求)。
这不是特定于移动赋值,将基类更改为仅支持复制赋值而不移动赋值将打印"copied"
两次:
struct V {
V& operator=(const V&) { puts("copied"); return *this; }
};
出现这种情况的原因完全相同, A::operator=(A&&)
和B::operator=(B&&)
都将分配基类。 编译器不会对此情况发出警告,因为两次执行复制操作(可能)只是次优,而不是错误。 对于移动分配,它可能会丢失数据。
如果您的虚拟基础实际上没有任何需要复制或移动的数据,或者只有可轻松复制的数据成员,那么使其仅支持复制不移动将抑制警告:
struct V {
V& operator=(const V&) = default;
};
此复制赋值运算符仍将被调用两次,但由于它不执行任何操作,因此没有问题。 什么都不做也没什么。
(GCC似乎比Clang更聪明一点,它没有警告虚拟基地的移动赋值操作符被调用两次,如果它是微不足道的,因为一个简单的移动相当于一个副本,所以不太可能是一个问题)。
如果虚拟基础确实具有需要在分配时复制的数据,那么使其复制而不是移动可能仍然是一个不错的选择,但这取决于类型和行为。 您可能需要在层次结构的每个级别显式定义复制和移动分配。 虚拟基础很棘手,难以正确使用,尤其是在复制或移动时。 使用虚拟基础处理类型作为可以轻松复制和移动的值类型可能是设计错误。
iostreams层次结构使用虚拟基础,但是要小心且正确地完成。 iostream类型是不可复制的,只能移动,派生类型显式定义移动赋值,以确保basic_ios<>
基类只更新一次。 具体来说, basic_iostream::operator=(basic_iostream&&)
仅在basic_istream
基础上运行,而不在basic_ostream
基础上运行。 上面例子的等价物是:
struct C : A, B {
C& operator=(C&& c) {
static_cast<A&>(*this) = static_cast<A&&>(c);
return *this;
}
};
直到C ++ 11,当rvalue引用和移动语义使得有用的语义成为可能时,Iostream根本不可复制。 如果你的类在C ++ 03中一直是可复制的,那么它可能已经是一个本来应该是不可复制的可疑设计,或者是经过精心编写的复制操作而不是隐式定义的复制操作。
简而言之,只要您有虚拟基础,就需要仔细考虑构造,分配和销毁的工作方式,以及复制和分配是否对类型有意义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.