[c++] virtual operator= WTF

elenangel

class base
{
public:
virtual base& operator=(const base& rhs) = 0;
};

class derived : public base
{
public:
base& operator=(const base& rhs);
private:
int data;
};

base& derived::operator =(const base& rhs)
{
if (this != &rhs)
{
data = dynamic_cast<const derived&>(rhs).data;
}
return *this;
}

int main(int , char *[])
{
derived d1,d2;
d1 = d2;
return 0;
}

~/cpp/virtual_assign_test-build-desktop/../virtual_assign_test/main.cpp:8: error: undefined reference to `base::operator=(base const&)'

apl13

Вот так комплируется:
class derived : public base
{
public:
base& operator=(const base& rhs);
derived &operator=(const derived &rhs) {
data = rhs.data; return *this;
}
private:
int data;
};

Догадаешься, почему?

elenangel

неправда твоя
 
~/cpp/virtual_assign_test-build-desktop/../virtual_assign_test/main.cpp:24: error: undefined reference to `vtable for derived'

P.S. а вот если пустые скобки {} к присваиванию предка добавить, то собирается.

elenangel

вот так работает. вопрос: как заставить компилер самому додуматься что d2 можно dynamic_cast в base& и вызывать реализованный оператор=?
class base
{
public:
virtual base& operator=(const base& rhs) = 0;
};


class derived : public base
{
public:
base& operator=(const base& rhs);
private:
int data;
};

base& derived::operator =(const base& rhs)
{
data = dynamic_cast<const derived&>(rhs).data;
return *this;
}

int main(int , char *[])
{
derived d1,d2;
//d1 = d2;
d1 = dynamic_cast<base&>(d2);
return 0;
}

Serab

вы не должны этого хотеть же

elenangel

а с обычными функциями нормально:

class fbase
{
public:
virtual fbase& assign(const fbase& rhs) = 0;
};

class fderived : public fbase
{
public:
virtual fbase& assign(const fbase& rhs);
private:
int data;
};

fbase& fderived::assign(const fbase& rhs)
{
data = dynamic_cast<const fderived&>(rhs).data;
return *this;
}
int main(int , char *[])
{
fderived fd1, fd2;
fd1.assign(fd2);
return 0;
}

operator= равнее чем "обычные" функции?

apl13

operator= равнее чем "обычные" функции?
Да бля.
Когда у тебя "только" base &derived::operator=(base const & у тебя еще есть неявный derived &derived::operator=(derived const &).
Из него и вызывается base &base::operator=(base const &). Поэтому компилятор и ругается.
Когда ты пишешь d1 = dynamic_cast(d2 вызывается derived::operator=(base const &).
operator= равнее в том смысле, что у него всегда есть как минимум один вариант, поэтому typecasting не происходит.

Maurog

вот так работает. вопрос: как заставить компилер самому додуматься что d2 можно dynamic_cast в base& и вызывать реализованный оператор=?
http://icu-project.org/docs/papers/cpp_report/the_assignment...
However, there’s another problem here. I remembered Taligent’s coding guidelines discouraging virtual assignment operators, so I went back to see why it recommended that. I wish I had done that before. It turns out Taligent’s guidelines weren’t hard and fast on the subject. Instead they point out that defining
virtual X& Y::operator=(const X& that);
won’t keep the compiler from defining
Y& Y::operator=(const Y& that);
In other words, an override of an inherited assignment operator doesn’t suppress the compiler-generated default assignment operator. You’d still have to do that manually by declaring it private and not giving it an implementation.
Instead, you’d have to define the default assignment operator to call the virtual one. In every class that inherits the virtual one.
http://ideone.com/3JHps

elenangel

Когда у тебя "только" base &derived::operator=(base const & у тебя еще есть неявный derived &derived::operator=(derived const &).
Из него и вызывается base &base::operator=(base const &). Поэтому компилятор и ругается.

так ведь даже если оно вызывается, я же прописал (перекрыл) его реализацию, хули оно говорит что я не прописал?

elenangel

Instead, you’d have to define the default assignment operator to call the virtual one. In every class that inherits the virtual one.
спасибо, я дотуда не дочитал, хотя эту страничку нагуглил :-)

apl13

я же прописал (перекрыл) его реализацию
Где?
Где у тебя в программе написано base `&base base::operator=(base const &rhs)'?

elenangel

undefined reference to `base::operator=(base const&)'

base& derived::operator =(const base& rhs)
{
if (this != &rhs)
{
data = dynamic_cast<const derived&>(rhs).data;
}
return *this;
}

разве место base::operator=(base const&) в vtable не должно указывать на перекрывающую его base& derived::operator=(const base&rhs) ?

Maurog

разве место base::operator=(base const&) в vtable не должно указывать на перекрывающую его base& derived::operator=(const base&rhs) ?
оно-то верно, но вызов оператора= невиртуальный происходит, судя по поведению
сгенерированный derived& derived::operator=(derived& o) вызывает base::operator=(o)

elenangel

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

apl13

разве место base::operator=(base const&) в vtable не должно указывать на перекрывающую его base& derived::operator=(const base&rhs) ?
Я плюсов не знаю, поэтому объявление "class Derived: public Base {" можно метафорически представить так:
class Base {
virtual Base &operator=(const Base &) = 0; // undefined pure virtual method
};

class Derived {
Base __parent;
protected:
<Base-protected-method-1>
...
public:
<Base-public-method-1>(<Args>) {
return __parent.<public-method-1>(<Args>);
}
...
public again:
__implicit Derived &operator =(const Derived &rhs) {
__parent = rhs.__private; // link-time error: undefined method
<other-fields> = rhs.<other-fields>;
}
};

elenangel

таблицу виртуальных методов забыл. в ней как раз-таки весь смысл.

apl13

Ну вот не в данном случае.

PITACHOK

а в чем проблема? насколько я понимаю, derived::operator=(const base &) здесь вообще нигде не используется. вызывается derived::operator=(const derived & который, в свою очередь пытается вызвать base::operator=(const base & но не находит его

elenangel

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

PITACHOK

а с чего ты взял, что derived::operator=(const derived &) у тебя виртуальный?

elenangel

а с чего ты взял, что я с чего-то взял, что derived::operator=(const derived &) виртуальный?
он не виртуальный. но он вызывает виртуальную функцию предка base::operator=(const base& так вот он ее вызывает невиртуальным образом, иначе не было бы ошибки связывания.

PITACHOK

а с чего ты взял, что он вызывает base::operator=(const base &) невиртуальным образом?

elenangel

читай тред внимательно. можешь позапускать приведенные примеры и сделать выводы.
Оставить комментарий
Имя или ник:
Комментарий: