1. ホーム
  2. c++

[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し

2022-01-18 01:42:29

質問

C++11のルールでは、6つのもの(デフォルトコンストラクタ、コピーコンストラクタ、移動コンストラクタ、コピー代入、移動代入、デストラクタ)がデフォルトで生成されることになっています。2番目の規則では、カスタムコピー、移動、デストラクタが定義されると、これらのデフォルトの操作は生成されません。 しかし、以下の私のコードでは、そのようなことはありません。しかし、このコードではコンパイルに失敗し、次のようなエラーが発生します。

call to implicitly deleted copy constructor of 'Uni'

Uni用のコピーコンストラクタを自作すると、すべてがうまくいきます。(参考までにコードにコメントをつけてあります)

何かご意見がありましたら、よろしくお願いします。

最後に、私はこれをMac、Xcode、LLVMコンパイラで実行しています。

ありがとうございました。

#include <iostream>

class A
{
public:
    A(int i) :num{i}
    {
        std::clog<< "ctor  A() num = " << num << "\n";

    }
    A( A const &aRef)
    :num{aRef.num}
    {
        std::clog << " copy ctor A( A const &aRef) num = " << num << "\n";
    }

    int value()
    {
        return num;
    }

private:
    int num;

};
class Uni
{

public:
    Uni(A* aptr) : up{aptr}
    {
        std::clog << " ctor Uni value = " << up.get()->value() << "\n";
    }
    /*Uni(Uni const &uRef)
    {
        std::clog << " copy ctor Uni copying obj pointed by unique_ptr\n";
        up.reset(uRef.up.get() ? new A{*uRef.up.get()} : nullptr);
    }*/
private:
    std::unique_ptr<A> up;

};

int main(int argc, const char * argv[])
{
    Uni one{new A{10}};
    Uni two{one}; //default copy ctor is implicitly deleted. why ?
}

解決方法は?

C++11の特殊メンバの自動生成ルールは、投稿されたような単純なものではありません。最も重要な違いは、メンバが暗黙的に宣言されていても、削除済みとして定義されているケースがあることです。あなたの場合はそうなっているのです。

C++11、[class.copy]§11。

クラスのデフォルトのコピー/移動コンストラクタ X が削除されたと定義されます (8.4.3)。 X があります。

  • 対応する自明でないコンストラクタを持つバリアントメンバと X はユニオンのようなクラスです。
  • クラスタイプの非静的データメンバである M (またはその配列) に適用される過負荷解消 (13.3) のため、コピー/移動ができない。 M の対応するコンストラクタがあいまいであったり、削除された関数やデフォルトのコンストラクタからアクセスできない関数であったりする場合です。
  • 直接または仮想基底クラス B に適用される過負荷の解決 (13.3) のためにコピー/移動できないものです。 B の対応するコンストラクタがあいまいであったり、削除された関数やデフォルトのコンストラクタからアクセスできない関数であったりする場合です。
  • デフォルトのコンストラクタから削除されるかアクセスできないデストラクタを持つ型の、直接または仮想の基底クラスまたは非静的データメンバ。
  • コピーコンストラクタの場合、rvalue参照型の非静的データメンバ、または
  • 移動コンストラクタの場合、非静的データメンバ、移動コンストラクタを持たない直接ベースクラスまたは仮想ベースクラスで、三重にコピー可能ではない型。

(強調)


より一般的には、自動生成されるクラスメンバに関するルールがあります。

  • クラスにユーザーが提供するコンストラクタがない場合、デフォルトのコンストラクタが宣言されます。

  • クラスがユーザー提供のコピーコンストラクタを持っていない場合、1 つが宣言されます。

  • クラスが { ユーザ提供のコピーまたは移動コンストラクタ、ユーザ提供のコピーまたは移動代入演算子、ユーザ提供のデストラクタ } のいずれも持たない場合、移動コンストラクタが宣言されます(ただし、以下の (*) を参照してください)。

  • クラスがユーザー提供のコピー代入演算子を持たない場合、その演算子が宣言されます。

  • クラスが { ユーザ提供のコピーまたは移動コンストラクタ、ユーザ提供のコピーまたは移動代入演算子、ユーザ提供のデストラクタ } のいずれも持たない場合、移動代入演算子が宣言されます(ただし、以下の (*) を参照してください)。

  • クラスがユーザー提供のデストラクタを持たない場合、デストラクタが宣言されます。

自動的に宣言されたメンバーは、defaulted(デフォルトの動作をする)として定義するか、deleted(使おうとするとエラーが発生する)として定義することができます。経験則では、"defaultedのバージョンが意味を持つ場合、defaultedとして定義されます。そうでなければ、deletedとして定義されます。

この文脈では、quot;makes sense" とは、削除された、あいまいな、アクセスできない、またはその他の不正な関数を呼び出そうとしないことを意味します(" 例えば、この回答の最初の部分で引用した標準のビットでは、コピーコンストラクタについて何が "make sense" でないかをリストアップしています。

さらに、自動的に宣言されたコピー・コンストラクタまたはコピー代入演算子は、クラスがユーザー提供の移動コンストラクタまたは移動代入演算子を持つ場合、削除されたものとして定義されます。

(*) 自動宣言された移動コンストラクタや移動代入演算子が削除と定義される場合、代わりに全く宣言されません。このルールは、そのようなクラスを移動しようとしたときに、エラーを発生させることなく、暗黙のうちにコピーに戻るようにするために存在します。