C++: чистый виртуальный оператор присваивания

почему, если у нас есть чистый виртуальный оператор присваивания в базовом классе, а затем мы реализуем этот оператор в производном классе, это дает ошибку компоновщика в базовом классе?

в настоящее время у меня есть только следующее объяснение на http://support.microsoft.com/kb/130486 , было сказано, что такое поведение является преднамеренным, поскольку обычные правила наследования не применяются.

мне непонятно, почему он по замыслу генерирует ошибку компоновщика? может кто-нибудь дать мне более четкое объяснение по этому поводу?

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

class __declspec(dllexport) BaseClass {
public:
    int memberA;
    virtual BaseClass& operator=(const BaseClass& rhs) = 0;
};

class __declspec(dllexport) DerivedClass : public BaseClass {
public:
    int memberB;
    DerivedClass():memberB(0) {}
    virtual BaseClass& operator=(const BaseClass& rhs) {
        this->memberA = rhs.memberA;
        this->memberB = 1;
        return *this;
    }
};

int main(void)
{
    DerivedClass d1;
    DerivedClass d2;

    BaseClass* bd1 = &d1;
    BaseClass* bd2 = &d2;

    *bd1 = *bd2;
}

код будет скомпилирован без ошибок без __declspec(dllexport) и/или без чистого виртуального объявления operator= в базовом классе.

без __declspec(dllexport) после присвоения *bd1 = *bd2; d1::memberB равно 1, но с __declspec(dllexport) d1::memberB остается без изменений

с __declspec(dllexport) и без чисто виртуального объявления, после присвоения *bd1 = *bd2; d1::memberB остается без изменений


person uray    schedule 21.09.2010    source источник
comment
может быть полезно опубликовать пример кода вместе с полной ошибкой компоновщика, которую выдает MSVC.   -  person Necrolis    schedule 21.09.2010
comment
FWIW, пункт 33 книги Скотта Мейерса Более эффективный C++ (Сделайте неконечные классы абстрактными) описывает, как обращаться с оператором = в иерархии наследования.   -  person Michael Kristofik    schedule 21.09.2010
comment
@necrolis: пример кода находится по указанной выше ссылке   -  person uray    schedule 21.09.2010
comment
@uray: В идеале вопросы по SO должны быть автономными, поскольку одна из целей — создать базу знаний для тех, кто придет после нас. Я видел, как вещи на microsoft.com со временем исчезают.   -  person David Thornley    schedule 21.09.2010
comment
@uray: помимо того, что упомянул Дэвид Торнли, иногда сам код MS может содержать ошибки или быть настолько общим, что он не слишком хорошо подходит для вашей конкретной ситуации. Я сделал вывод, что пример взят из вашего кода, который может иметь ошибку, которой нет у MS.   -  person Necrolis    schedule 21.09.2010
comment
вопрос отредактирован (добавлен пример кода)   -  person uray    schedule 21.09.2010


Ответы (3)


Из раздела 12.8 стандарта:

13 Неявно определенный оператор присваивания копирования для класса X выполняет поэлементное присваивание его подобъектов. Прямые базовые классы X назначаются первыми в порядке их объявления в списке базовых спецификаторов, а затем назначаются непосредственные нестатические элементы данных X в том порядке, в котором они были объявлены в определении класса. Каждый подобъект назначается способом, соответствующим его типу:

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

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

person Steve M    schedule 21.09.2010
comment
Я думаю, что он говорит об операторе =, а не о конструкторе копирования. Для копировщика - виртуальный и чистый виртуальный должны быть немедленной ошибкой компилятора, поскольку для них это действительно не имеет смысла. - person Lou Franco; 21.09.2010
comment
Его ссылка посвящена оператору =, и именно об этом мой пост. - person Steve M; 21.09.2010
comment
Из OP then we implement that operator on the derived class я собираюсь сказать, что у него есть явный оператор присваивания копии в дочернем элементе (не неявный, который ваш ответ будет правильно охватывать), и нет никаких указаний на то, что он вызывает или не вызывает родительскую (чисто виртуальную) копию назначение. - person Mark B; 21.09.2010
comment
Верно, но в комментариях он явно указал Necrolis на ссылку. Тем не менее, если бы он определил A &operator= (A&) в B, присваивание все равно вызвало бы неявную версию, поскольку оба операнда имеют тип B, а оператор присваивания копирования базового класса всегда скрыт оператором присваивания копирования производного класса (12.8.10). ). - person Steve M; 21.09.2010

оператор= не наследуется. Ваш код не имеет смысла на C++, поэтому компиляторы могут выдать любую ошибку, какую захотят.

Из статьи базы знаний, на которую вы указали: http://support.microsoft.com/kb/130486

Поскольку оператор = не наследуется, любое объявление оператора = в базовом классе не используется и не нужно. Не объявляйте оператор = в базовом классе.

Вероятно, это просто побочный эффект того, как они компилируют, и они просто сообщают вам, что не считают это ошибкой, поэтому нет необходимости это исправлять. «По задумке» не обязательно означает, что они специально решили, что эта ошибка компоновщика является правильным сообщением об ошибке для этой ситуации — код неверный, вы получаете ошибку, поэтому с их точки зрения — они Выполнено.

person Lou Franco    schedule 21.09.2010
comment
Статья базы знаний ОП. я обновлю - person Lou Franco; 21.09.2010

В примере кода:

class A
{
public :
   // To workaround LNK2001, comment the following line.
   virtual const A& operator=( const A& f ) = 0;
};

class B : public A
{
public :
   const A& operator=( const A& g ) {return g;}
};

B aB1, aB2;

int /*void*/ main( void )
{
   aB2 = aB1;
}

строка aB2 = aB1 не вызывает const A& B::operator=(const A&), а вместо этого вызывает автоматически предоставленный B& operator=(const B&);, который, в свою очередь, использует оператор присваивания для назначения базовой части класса. Но когда дело доходит до связывания, оказывается, что это никогда не было реализовано.

person UncleBens    schedule 21.09.2010
comment
Оператор присваивания был переопределен. Подобно объявлению любого конструктора (не шаблонного) эффективно деактивирует неявно определенный конструктор по умолчанию и конструктор копирования, объявление любого operator= (не шаблонного) деактивирует неявно определенный оператор присваивания. - person Matthieu M.; 21.09.2010
comment
@Matthieu: Квалифицируется ли это как назначение копирования? И ведь struct X { X& operator=(int);}; не мешает мне присвоить один X другому, как и должно, если любая перегрузка operator= отключила дефолтный. - Точно так же любой конструктор не деактивирует конструктор копирования, а только допустимый пользовательский конструктор копирования. - person UncleBens; 22.09.2010
comment
вы правы, но при использовании B& operator=(A const&) эта версия вызывается вместо версии по умолчанию (по крайней мере, с VC++10)... теперь я весь запутался, и мне уже слишком поздно разбираться в этом... - person Matthieu M.; 23.09.2010