3

我有一个包含三个类(A、B 和 C)的类层次结构。A 和 B 是基类,使用派生类型进行参数化。C类派生自A和B。

using super::operator=B 类为 A 类型的对象提供了一个赋值运算符,而 C 类通过声明继承了这个赋值运算符。

当我从 A 类型的对象定义类 B 中的构造函数时,我得到错误: 两个重载在 Visual Studio 2013 中具有相似的转换(C2666),但在 gcc (4.8.2) 中没有任何错误或警告、clang (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 中编译而不会出现错误——但这不是解决问题的方法。

我的问题是:这个结构是否符合标准,或者错误消息是否正确?像explicitB 中的复制构造函数这样的说明符是否有助于解决问题?

顺便说一句:在 Visual Studio 中,我收到警告:指定了多个赋值运算符(C4522),因为 C 类中的复制赋值运算符。有人可以向我解释一下,为什么这应该是一个问题?

4

1 回答 1

1

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

预期的行为是什么:

该语句c=a;使用operator= 您在 中定义的B,因为 anA<C>不一定是 a C。因此,让我们通过手动进行类型替换来写下 of 的声明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 更改为更简约的形式,您仍然会收到错误消息

struct C : public B<C>   // single inheritance
{   using B<C>::operator=; };  // nothing else

实际上问题是由构造函数引起的B(A<M> const&)

  • 将其注释掉,代码将编译
  • 使其明确,代码也将编译

MSVC 错误地识别了成员函数隐式特化的第二个潜在候选者。由于此构造函数允许从 A<M>to隐式转换B<C>,因此候选者是:

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

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

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

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

顺便一提:

关于多个赋值运算符的警告只是一个信息。显然 MS 认为这可能是导致错误的常见原因。现在到核心问题...

于 2015-04-01T22:00:11.990 回答