我有一个包含三个类(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)
由于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错误地为成员函数的隐式专业识别了第二个潜在候选者。由于此构造函数允许从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/

    10-12 19:02