Почему следующий код компилируется, хотя у меня есть неопределенные функции-члены? - PullRequest
8 голосов
/ 28 февраля 2012

Я был на полпути, работая над этим фрагментом кода, и подумал , что он явно не собирается компилировать до нажатия кнопки сборки. Я был удивлен, что он не только скомпилирован, но и связан и работает.

Если бы я догадался, я бы сказал, что SFINAE отвечает за его компиляцию ... это так?

struct BaseClass
{
public:
  BaseClass() {}

  template<typename T>
  BaseClass(const T& a_other)
  {
    int i = 0; // for break point
  }

  template<typename T>
  BaseClass& operator= (const T& a_other)
  {
    int i = 0; // for break point
    return *this;
  }

private:

  BaseClass(const BaseClass& a_other); // Does not have a definition
  BaseClass& operator= (const BaseClass& a_other); // Does not have a definition

};

struct MyClass : public BaseClass
{
};

int main()
{
  MyClass i, j;
  i = j;

  return 0;
}

РЕДАКТИРОВАТЬ : я использую Visual-C ++ 2008, возможно, это странная причуда VS

Ответы [ 5 ]

3 голосов
/ 28 февраля 2012

Код не является законным.

i = j вызывает неявно определенный оператор назначения копирования в MyClass. Эта функция вызывает оператор присваивания для каждого из его подобъектов, включая прямые базовые классы [class.copy 12.8 p28].

Если вы добавите код в оператор назначения копирования для BaseClass, вы увидите, где VS работает неправильно:

  template<typename T>
  BaseClass& operator= (const T& a_other)
  {
    std::cout << typeid(T).name() << '\n';
    int i = 0; // for break point
    return *this;
  }

Для меня это выводит "struct MyClass". VS вызывает оператор присваивания BaseClass, передавая параметр, полученный непосредственно в MyClass:operator=, а не только субобъект BaseClass в j.

SFINAE не вступает в игру, потому что функции шаблонов не работают. VS просто неправильно генерирует оператор неявного копирования.

Подводя итог: VS генерирует неявный оператор присваивания копии как

MyClass &operator=(const MyClass& rhs) {
    static_cast<BaseClass&>(*this).operator=(rhs);
    return *this;
}

Когда это должно быть:

MyClass &operator=(const MyClass& rhs) {
    static_cast<BaseClass&>(*this).operator=(static_cast<const BaseClass&>(rhs));
    return *this;
}
1 голос
/ 28 февраля 2012

Выстрел в темноте: компилятор создает базовый класс 'operator = с T = MyClass.Теперь я знаю, является ли это законным или даже обязательным, но это имеет определенный вид смысла: автоматически сгенерированный код для operator = по сути выглядит так (ну, псевдокод):

MyClass& operator =(MyClass const& other) {
    BaseClass::operator =(other);
    return *this;
}

Теперь компилятор находит, что BaseClass::operator =<MyClass>(MyClass const&) является лучшим соответствием, и создает его.

0 голосов
/ 28 февраля 2012

MyClass будет использовать неявно определенный оператор присваивания копии, так как нет объявленной пользователем версии.В соответствии со стандартом это неявное назначение копирования будет выполнять следующее (C ++ 03 12.8 / 13 «Копирование объектов класса»):

  • Каждый подобъект назначается способом, соответствующим его типу: - если подобъект относится к типу класса, то для этого класса используется оператор присваивания копии (как при явной квалификации; то есть при игнорировании любых возможных виртуальных переопределяющих функций в более производных классах);

Обратите внимание, что в 12.8 / 9 стандарт определяет объявленный пользователем оператор копирования как «нестатическая не шаблонная функция-член класса X с ровно одним параметром типа X, X &, const X &, volatileX & or const volatile X & "(выделено мной).

Таким образом, согласно стандарту, шаблонная функция

  template<typename T>
  BaseClass& operator= (const T& a_other);

не должна вызываться оператором неявного копирования MyClass.MSVC действует здесь нестандартно.GCC правильно диагностирует это:

C:\temp\test.cpp: In member function 'MyClass& MyClass::operator=(const MyClass&)':
C:\temp\test.cpp:29:14: error: 'BaseClass& BaseClass::operator=(const BaseClass&)' is private
C:\temp\test.cpp:33:8: error: within this context
C:\temp\test.cpp: In function 'int main()':
C:\temp\test.cpp:40:7: note: synthesized method 'MyClass& MyClass::operator=(const MyClass&)' first required here 
0 голосов
/ 28 февраля 2012

Компилятор автоматически сгенерирует (по умолчанию) конструктор копирования для MyClass, поскольку он не определен. Если вы измените тип i и j с MyClass на BaseClass, вы увидите ожидаемую ошибку, так как компилятор затем попытается связать частный неосуществленный оператор присваивания.


Если углубиться в это с помощью MSVC 2010 Ultimate SP1, мы увидим точную причину:

MyClass::operator=:
00201230  push        ebp  
00201231  mov         ebp,esp  
00201233  push        ecx  
00201234  mov         dword ptr [ebp-4],ecx  
00201237  mov         eax,dword ptr [__that]  
0020123A  push        eax  
0020123B  mov         ecx,dword ptr [this]  
0020123E  call        BaseClass::operator=<MyClass> (202130h)  
00201243  mov         eax,dword ptr [this]  
00201246  mov         esp,ebp  
00201248  pop         ebp  
00201249  ret         4 

оператор присваивания вызывается для MyClass с помощью BaseClass::=<T> с использованием MyClass в качестве оператора типа по умолчанию через MyClass es оператор копирования по умолчанию, независимо от того, является ли это ошибкой или из-за MSVC соответствует MS и стандарту C ++ решать.

0 голосов
/ 28 февраля 2012

Ну, так как он не может вызвать BaseClass::operator=(const BaseClass&) (из сгенерированного по умолчанию MyClass::operator= того, чего он стоит), потому что он приватный, он просто вызывает template<typename T> BaseClass::operator(const T &) с T=BaseClass.Таким образом, нет вызова неопределяемой функции.

Я полагаю, что ситуация была бы другой, если бы другие были общедоступными, и в этом случае компилятор предпочел бы их над шаблоном, но так как он не может их видеть, когдабудучи закрытыми, версии шаблонов совпадают одинаково.

...