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

Posted

技术标签:

【中文标题】赋值运算符重载有类似的转换(仅在 VS 中)【英文标题】:Assignment operator overloads have similar conversions (only in VS) 【发布时间】:2015-06-02 12:50:02 【问题描述】:

我有一个包含三个类(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】:

GCC 和 CLANG 正确,MSVC 错误:

预期的行为是什么:

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

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

由于aA&lt;C&gt;,这个模板的明显隐式实例化候选将是:

 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&lt;M&gt; const&amp;)引起的:

注释掉,代码就可以编译了 明确说明,代码也会编译

MSVC 错误地识别了成员函数隐式特化的第二个潜在候选者。由于此构造函数允许将 A&lt;M&gt; 隐式转换为 B&lt;C&gt;,因此候选对象是:

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

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

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

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

顺便说一句:

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

【讨论】:

以上是关于赋值运算符重载有类似的转换(仅在 VS 中)的主要内容,如果未能解决你的问题,请参考以下文章

重载运算与类型转换——基本概念,输入和输出运算符,算术和关系运算符,赋值运算符,下标运算符,递增和递减运算符,成员访问运算符

面向对象程序设计-C++_课时30运算符重载——基本规则_课时31运算符重载——原型_课时32运算符重载——赋值_课时33运算符重载——类型转换

2 个重载有类似的转换 - 内置运算符 integer[pointer-to-object]

C++的=重载问题,怎样为两个有相同成员的类赋值

C++中赋值运算操作符和=重载有啥区别?

类和对象—4