赋值运算符重载具有类似的转换(仅在VS中)

时间:2015-03-29 14:33:59

标签: c++ inheritance copy-constructor assignment-operator using-declaration

我有一个包含三个类(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类中的复制赋值运算符。有人可以放弃我,为什么这应该是一个问题?

1 个答案:

答案 0 :(得分:1)

GCC和CLANG是正确的,MSVC错了:

预期的行为是什么:

语句c=a;使用您在operator=中定义的B,因为A<C>不一定是C。所以让我们通过手动进行类型替换来写下operator= B<C>的声明:

template <class M> 
C& operator=(A<M> const& rhs)

由于aA<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错误地识别出成员函数隐式特化的第二个potental候选者。由于此构造函数允许隐含地从A<M>转换为B<C>,因此候选者是:

 C& operator=(B<C> const& rhs)

但是根据C ++标准,编译器根本不应该设想这个:

  

14.8.1 / 6:将对函数参数执行隐式转换,将其转换为相应函数的类型   如果参数类型不包含模板参数,则参数   参与模板参数演绎。

所以这显然是MSVC的一个错误。

顺便说一下:

有关多个受让人操作员的警告是just an information。显然,MS认为这可能是导致错误的常见原因。而现在是核心问题......