我有一个包含三个类(A,B和C)的类层次结构。 A和B是基类,使用派生的Type参数化。 C类是从A和B派生的。
类B为类型A的对象提供了一个赋值运算符,而类C用using super::operator=
声明继承了该赋值运算符。
当我从A类型的对象在类B中定义构造函数时,我收到错误:,两个重载在Visual Studio 2013中具有相似的转换(C2666),但在gcc(4.8。 2),c(3.4)和英特尔icc(Studio 2015)。 (与-Wall -pedantic
一起编译)
这里是简化的示例:
template <class Model> struct A {};
template <class Model> struct B
{
B() {}; // default constructor
// copy constructor for objects of type A
template <class M>
B(A<M> const&) {}
// assignment operator for objects of type A
template <class M>
Model& operator=(A<M> const& rhs)
{
return static_cast<Model&>(*this);
}
};
struct C : public B<C>, public A<C>
{
typedef B<C> super;
// copy assignment operator
C& operator=(C const& rhs) { return *this; }
// adopt assignment operator for A<C> from super-class
using super::operator=;
};
int main()
{
C c;
A<C> a;
c = a;
}
如果我将模板化类A替换为非模板化类,那么它也可以在Visual Studio中编译而不会出现错误-但这不是解决问题的方法。
我的问题是:这个结构是否符合标准规范,或者错误消息正确吗? B中的复制构造函数之类的类似于
explicit
的说明符是否有助于解决问题?顺便说一句:在Visual Studio中,我得到了警告:由于类C中有复制赋值运算符,所以指定了多个赋值运算符(C4522)。有人可以向我举例说明,为什么这应该是一个问题?
最佳答案
GCC和CLANG正确,MSVC错误:
预期的行为是什么:
语句c=a;
使用您在operator=
中定义的B
,因为A<C>
不一定是C
。因此,让我们通过手动进行类型替换来写下operator=
的B<C>
的声明:
template <class M>
C& operator=(A<M> const& rhs)
由于a
是A<C>
,因此此模板的明显隐式实例化候选者为: C& operator=(A<C> const& rhs)
实际上,这是唯一可能的实例化(您可以通过显示typeinfo来验证GCC是否使用了它)。MSVC打算做什么?
如果您将C类简化为更简单的形式,您仍然会得到error:
struct C : public B<C> // single inheritance
{ using B<C>::operator=; }; // nothing else
实际上,问题是由构造函数B(A<M> const&)
引起的:MSVC错误地为成员函数的隐式专业识别了第二个潜在候选者。由于此构造函数允许从
A<M>
隐式转换为B<C>
,因此候选者为: C& operator=(B<C> const& rhs)
但是根据C++标准,编译器完全不应该考虑到这一点:因此,这显然是MSVC的错误。
顺便说一句:
有关多个赋值运算符的警告为just an information。显然,MS认为这可能是导致错误的常见原因。现在到核心问题...
关于c++ - 赋值运算符重载具有相似的转换(仅在VS中),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29330391/