我有一个包含三个类(A,B和C)的类层次结构。 A和B是基类,使用派生类型进行参数化。 C类来自A和B.
B类为A类对象提供赋值运算符,C类继承了using super::operator=
声明的赋值运算符。
当我从类型A的对象在B类中定义构造函数时,我在Visual Studio 2013中得到错误: 两个重载具有类似的转换(C2666),但是我没有在gcc(4.8.2),clang(3.4)和intel 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中,我得到了警告:指定的多个赋值运算符(C4522),因为C类中的复制赋值运算符。有人可以放弃我,为什么这应该是一个问题?
答案 0 :(得分:1)
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是否使用它)。
如果您将C类简化为更简约的形式,您仍然可以获得error:
struct C : public B<C> // single inheritance
{ using B<C>::operator=; }; // nothing else
实际上问题是由构造函数B(A<M> const&)
引起的:
MSVC错误地识别出成员函数隐式特化的第二个potental候选者。由于此构造函数允许隐含地从A<M>
转换为B<C>
,因此候选者是:
C& operator=(B<C> const& rhs)
但是根据C ++标准,编译器根本不应该设想这个:
14.8.1 / 6:将对函数参数执行隐式转换,将其转换为相应函数的类型 如果参数类型不包含模板参数,则参数 参与模板参数演绎。
所以这显然是MSVC的一个错误。
顺便说一下:
有关多个受让人操作员的警告是just an information。显然,MS认为这可能是导致错误的常见原因。而现在是核心问题......