5

今日、理解できないコードに出くわしました。次の例を検討してください。

#include <iostream>
#include <string>

class A
{
public:
    template <class Type>
    Type& operator=(Type&& theOther)
    {
        text = std::forward<Type>(theOther).text;

        return *this;
    }

private:
    std::string text;
};

class B
{
public:
    B& operator=(B&& theOther)
    {
        text = std::forward<B>(theOther).text;

        return *this;
    }

private:
    std::string text;
};

int main()
{
    A a1;
    A a2;
    a2 = a1;

    B b1;
    B b2;
    b2 = b1;

    return 0;
}

コンパイル時に、MinGW-w64/g++ 10.2 は次のように述べています。

..\src\Main.cpp: In function 'int main()':
..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const B&)'
   41 |  b2 = b1;
      |       ^~
..\src\Main.cpp:19:7: note: 'B& B::operator=(const B&)' is implicitly declared as deleted because 'B' declares a move constructor or move assignment operator
   19 | class B
      |       ^
mingw32-make: *** [Makefile:419: Main.o] Error 1

エラーメッセージを完全に理解しています。しかし、 class で同じメッセージが表示されない理由がわかりませんA。テンプレート化された移動代入演算子も移動代入演算子ではありませんか? では、なぜコピー代入演算子が削除されないのでしょうか? これはよく書かれたコードですか?

4

2 に答える 2

5

@songyuanyao の標準ベースの回答を補完するものとして: これらの種類の言語規則は、MISRA/AUTOSAR (セーフティ クリティカルな C++ 開発のための言語ガイドライン) などのガイドラインが次のような「開発者の混乱を避ける」規則を持つ一般的な理由です。

( AUTOSAR C++14 ガイドラインより)

ルール A14-5-1 (必須、実装、自動化)

テンプレート コンストラクターは、外側のクラス型の単一の引数のオーバーロード解決に参加してはなりません。

根拠

テンプレート コンストラクターは決してコピー コンストラクターまたはムーブ コンストラクターではないため、テンプレート コンストラクターが似ていて混乱しやすい場合でも、コピー コンストラクターまたはムーブ コンストラクターの暗黙的な定義を妨げません。同時に、コピー操作または移動操作は必ずしもコピー コンストラクターまたは移動コンストラクターのみを使用するわけではありませんが、通常のオーバーロード解決プロセスを経て、使用するのに最適な一致する関数を見つけます。これは、次の場合に混乱を招く可能性があります。

  • コピー/移動コンストラクターのように見えるテンプレート コンストラクターが選択されていない
  • コンパイラが暗黙のコピー/移動コンストラクターを生成し、コピー/移動コンストラクターよりも優先してテンプレート コンストラクターが選択されるため、コピー/移動操作の場合

これらの紛らわしい状況を避けるために、テンプレート コンストラクターは、コピー/移動操作のためにテンプレート コンストラクターが選択されるのを避けるために、囲んでいるクラス型の単一の引数のオーバーロード解決に参加してはなりません。また、コンストラクターがコピー/移動コンストラクターではないこと、およびコピー/移動コンストラクターの暗黙的な生成を妨げないことも明確にします。

ルール M14-5-3 (必須、実装、自動化)

コピー代入演算子は、ジェネリック パラメーターであるパラメーターを持つテンプレート代入演算子がある場合に宣言する必要があります。

つまり、テンプレートの copy/move ctor/assignment 演算子が暗黙的に生成されたものを抑制しない (/5 のルールを「アクティブ化」しない) ことは、開発者にとって驚くべきことです。通常、SFINAE を使用して、囲んでいるクラスの単一の引数に対してオーバーロードをアクティブにできるようにすることで、テンプレート化された ctor/assignment op がコピー/移動 ctor/assignment であるかのように動作しないようにする必要があります。

于 2021-10-28T09:18:00.737 に答える