operator=()をoverrideできるのか?

operator=()をoverrideできるのか?
できるかできないかであれば、できる。
しかし、しない方が良いだろう。

ベースクラスのポインターを保持する関数などで派生クラスを含めたコピーをしたい場合どうなるか。
確認をした。
なお今回のお題では呼び出し側でのキャストはなしにする(複数の派生クラスを使っている場合を想定)。

// ベースクラス
class TBase
{
  protected:
    int n;
  public:
    TBase( int s ) : n(s) {}
    TBase(            const TBase&  ) = default;
    TBase(                  TBase&& ) = default;
    TBase& operator=( const TBase&  ) = default;
    TBase& operator=(       TBase&& ) = default;
    virtual ~TBase()                  = default;

            int get_n() { return n; }
    virtual int get_m() = 0;
};
//---------------------------------------------------------------------------
// 派生クラス
class TChild1 : public TBase
{
  protected:
    int m;
  public:
    TChild1( int s, int p ) : TBase(s), m(p) {}
    TChild1(            const TChild1&  ) = default;
    TChild1(                  TChild1&& ) = default;
    TChild1& operator=( const TChild1&  ) = default;
    TChild1& operator=(       TChild1&& ) = default;
    virtual ~TChild1()                    = default;

    virtual int get_m() override { return m; }
};

上のような環境で、ベースクラスのポインターでコピーをするとどうなるか。

  TBase* s1 = new TChild1(2, 12);
  TBase* s2 = new TChild1(3, 13) ;

  *s2 =  *s1; // s2->n=2, s2->m=13

operator=()の呼び出しは、TBaseのoperator=()が用いられる。TChild1のoperator=()は引数が違うので用いられない。
TBaseとTChildのoperator=()にvirtualをつけても、引数が違うので別の仮想関数になり、やはりTChild1のoperator=()は呼び出しされない。
もちろん下のように両方のポインターをキャストすればできるが、その場合はvirtualを付けなくても変わらない。
但し、キャストできたか確認が必要になる。

  TBase* s1 = new TChild1(2, 12);
  TBase* s2 = new TChild1(3, 13) ;

  *dynamic_cast<TChild1*>(s2) =  *dynamic_cast<TChild1*>(s1); // s2->n=2, s2->m=12

では、TBaseのoperator=()をvitrualにして、派生関数側でも定義すればどうか?

class TBase
{
  protected:
    int n;
  public:
    TBase( int s ) : n(s) {}
    TBase(            const TBase&  ) = default;
    TBase(                  TBase&& ) = default;
    virtual TBase& operator=( const TBase&  ) = default;
    virtual TBase& operator=(       TBase&& ) = default;
    virtual ~TBase()                  = default;

            int get_n() { return n; }
    virtual int get_m() = 0;
};
//---------------------------------------------------------------------------
class TChild2 : public TBase
{
  protected:
    int m;
  public:
    TChild2( int s, int p ) : TBase(s), m(p) {}
    TChild2(            const TChild2&  ) = default;
    TChild2(                  TChild2&& ) = default;
    TChild2& operator=( const TChild2&  ) = default;
    TChild2& operator=(       TChild2&& ) = default;
    virtual ~TChild2()                    = default;

    virtual TBase& operator=( const TBase&  lhs ) override { const TChild2&  Lhs = static_cast<const TChild2& >(lhs); return operator=( Lhs ); };

    virtual int get_m() override { return m; }
};

おなじく、ベースクラスのポインターでコピーをするとどうなるか。

  TBase* s1 = new TChild2(2, 12);
  TBase* s2 = new TChild2(3, 13) ;

  *s2 =  *s1; // s2->n=2, s2->m=12

operator=()の呼び出しは、TChild2のoperator=( const TBase& )が用いられる。
この書き方で、意図した結果を得られることが判った。
ただし、この書き方をすると保守管理が大変なことが予想できる。=の挙動が場合によって変わる可能性もあるからだ。
例えば、s1が別の派生クラスであった場合、結果は悲惨なことになる。

やはり別関数を導入した方が良いのだろう。

class TBase
{
  protected:
    int n;
  public:
    TBase( int s ) : n(s) {}
    TBase(            const TBase&  ) = default;
    TBase(                  TBase&& ) = default;
    TBase& operator=( const TBase&  ) = default;
    TBase& operator=(       TBase&& ) = default;
    virtual ~TBase()                  = default;

            int get_n() { return n; }
    virtual int get_m() = 0;
    virtual void assign( const TBase* pBase ) { operator=( *pBase ); }
};
//---------------------------------------------------------------------------
class TChild3 : public TBase
{
  protected:
    int m;
  public:
    TChild3( int s, int p ) : TBase(s), m(p) {}
    TChild3(            const TChild3&  ) = default;
    TChild3(                  TChild3&& ) = default;
    TChild3& operator=( const TChild3&  ) = default;
    TChild3& operator=(       TChild3&& ) = default;
    virtual ~TChild3()                    = default;

    virtual int get_m() override { return m; }
    virtual void assign( const TBase* pBase ) override { 
      TChild3* pChild = dynamic_cast<const TChild3*>(pBase);
      if( pChild3 == nullptr ) throw false; // 例外を発生させる
      operator=( *pChild3 ); 
    }
};

TBaseのassignは使わないのであれば純粋仮想関数にしても良いだろう。

  TBase* s1 = new TChild3(2, 12);
  TBase* s2 = new TChild3(3, 13) ;

  s2->assign( s1 ); // s2->n=2, s2->m=12

関数名を適切に指定することで、何をしたいのかもよく判る。