参照メンバーを持つデフォルトのコピー代入演算子?[複製]

Aug 21 2020

Visual Studio 2017でハイブリッドレガシーアプリケーション(C ++ / C#)に取り組んでいます。現在、別の問題の診断に役立つように、既存のクラスにログを追加しようとしています。問題のクラスはかなり単純です。

class CAnExampleClass : CAnExampleBaseClass
{
public:
    CAnExampleClass();
    virtual ~CAnExampleClass(void);

    // dozens of public methods
private:
    // a few private methods
public:
    // dozens of public member variables, all intrinsic types or custom types with copy constructors or arrays thereof
private:
    class AnExampleChildImpl;
    mutable std::shared_ptr<AnExampleChildImpl> _pImpl;
    // a couple of friend classes
    bool _anExampleFlag;
public:
    static const int NumberOfItems = 15;
}

実装では、コンストラクターは次のように初期化リストのプライベートメンバーを初期化します。

CAnExampleClass::CAnExampleClass()
    : _anExampleFlag(false)

アプリケーションの他の場所で確立されたパターンに従って、ロガーのクラスにメンバーを追加しました。

private:
    ILog& _log;

そして、コンストラクターの初期化リストでこのメンバーを初期化しました。

CAnExampleClass::CAnExampleClass()
    : _anExampleFlag(false), _log(CMyLog::GetLogger("log_name"))

ロガーはかなり単純です:

class ILog
{
    ...
}
...
class CMyLog
{
public:
    ...
    static ILog& GetLogger(const char *loggerName);
    ...
}

ただし、コンパイラはエラーを報告しています。

Error   C2280   'CAnExampleClass &CAnExampleClass::operator =(const CAnExampleClass &)': attempting to reference a deleted function ...

結局のところ、このクラスの既存のインスタンスを新しいインスタンスにコピーするために、コードがデフォルトのコピー代入演算子に依存しているアプリケーション全体でかなりの数のポイントがあります。また、ロガーがすでに使用されている他のケースでは、コピー代入演算子を使用してクラスをコピーする試みがない場合もあります。

したがって、私の質問は、CAnExampleClass各(パブリック)メンバーと配列のカスタムコピー代入演算子を定義して、新しいインスタンスにコピーする代わりの方法はありますか?導入された参照メンバーがコンストラクターの初期化リストで初期化されるときにこれが必要になるのは私にはやり過ぎのようです。その場合、デフォルトのコピー代入演算子が以前のように機能するのを妨げているのは何ですか?初期化リストが参照メンバーを処理しているため、基本的にデフォルトの実装とまったく同じことを行うときに、演算子のカスタム実装を定義する必要があるのはなぜですか?デフォルトのコピー割り当て演算子に参照メンバーを無視させる方法、またはカスタム演算子の実装からデフォルトのコピーロジックを呼び出す方法はありますか?

この問題に対処するためにこのクラスの外部のコードを変更することは現実的なオプションではありません(アプリケーションが大きすぎる、古い、扱いにくいなど)理想的には、より簡単な方法がある場合は、カスタム代入演算子の実装オプションを避けたいと思います。 (明らかに)これらすべてのメンバー割り当てを手動で再作成する必要がなくなりますが、チームの別の開発者が後でこのクラスにメンバーを追加した場合に、コピー割り当て演算子の更新を誤って忘れてしまう可能性も回避できます。その演算子に依存するコードに奇妙な論理エラーがあります。

回答

bitmask Aug 21 2020 at 12:41

デフォルトのコピー代入演算子が以前のように機能するのを妨げているのは何ですか?

CAnExampleClassタイプのメンバーを紹介しましたILog&。参照は再装着できません。初期化されると、変更することはできません。シングルトンパターンのために、のインスタンスILogは常に同じですが、参照メンバーがあると、デフォルトの代入メンバー関数が自動的に削除されます。

参照メンバーを持つすべてのクラスに代入演算子を実装しますが、これは実行可能ではないようです。または、std::reference_wrapper次のいずれかを優先して生の参照を削除します。

std::reference_wrapper<ILog> _log; // <- reseatable reference

または、メンバーを完全に削除しCMyLog::GetLogger("log_name")、ログオブジェクトが必要なときに常に呼び出して、静的プライベートメンバー関数にカプセル化します。

static ILog& logger() {
  // this ONLY works if GetLogger always returns the same object
  return CMyLog::GetLogger("log_name");
}