Перегрузки оператора присваивания имеют аналогичные преобразования (только в VS)

У меня есть иерархия классов с тремя классами (A, B и C). A и B являются базовыми классами, параметризованными производным типом. Класс C является производным от обоих, A и B.

Класс B предоставляет оператор присваивания для объектов типа A, а класс C наследует этот оператор присваивания с объявлением using super::operator=.

Когда я определяю конструктор в классе B из объектов типа A, я получаю Ошибку: две перегрузки имеют похожие преобразования (C2666) в Visual Studio 2013, но я не не получают никаких ошибок или предупреждений в 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 без ошибок, но это не так, как это можно решить.

Мой вопрос: правильно ли построена эта конструкция в том смысле, что она соответствует стандарту, или сообщение об ошибке верно? Помогает ли спецификатор типа explicit для конструктора копирования в B решить проблему?

Кстати: в Visual Studio я получаю Предупреждение: указано несколько операторов присваивания (C4522) из-за оператора присваивания копирования в классе C. Может ли кто-нибудь объяснить мне , почему это должно быть проблемой?


person spraetor    schedule 29.03.2015    source источник


Ответы (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)

На самом деле это единственный возможный экземпляр (вы можете убедиться, что GCC использует его, отобразив информацию о типе).

Что пытается сделать MSVC?

Если вы измените упрощенный класс C на еще более минималистичный вид, вы все равно получите ошибка:

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++ это вообще не должно быть предусмотрено компилятором:

14.8.1/6: Неявные преобразования будут выполняться для аргумента функции, чтобы преобразовать его в тип соответствующего параметра функции, если тип параметра не содержит параметров-шаблонов, которые участвовать в выводе аргумента шаблона.

Так что это явно ошибка MSVC.

Кстати:

Предупреждение о множественных операторах назначения — это просто информация. По-видимому, MS предполагает, что это может быть частой причиной ошибок. А теперь к основному вопросу...

person Christophe    schedule 01.04.2015