【C++】コピーコンストラクタの自動生成について教えてください。


通常、あるクラスに対してコピーコンストラクタが定義されていない場合、コピーコンストラクタは必要に応じて自動生成されるものと思います。

しかし、必要なのにコピーコンストラクタを明示的に定義しておかないと自動生成が行われないケース(つまり、コンパイル時にコピーコンストラクタが見つからないというエラーが出る)があって、不思議に思っているのですが、そのような場合には一般的にどのようなケースが考えられるでしょうか?

なお、たとえば当該クラスの親クラスのコピーコンストラクタがprivateになっているような場合は、「コピーコンストラクタが見つからない」ではなく、「コピーコンストラクタにアクセスできない」というエラーになりますので、そのようなケースは除外してください。

また、コンパイラの設定で自動生成を禁止している、というケースも除外してください。

一応、当方のコンパイル環境はC++Builder5ですが一般的なC++の話で結構です。

回答の条件
  • 1人3回まで
  • 登録:2007/02/18 13:45:47
  • 終了:2007/02/18 18:06:02

ベストアンサー

id:Bookmarker No.1

しおり回答回数191ベストアンサー獲得回数342007/02/18 17:23:19

ポイント60pt

ひとまず1例として見つけたのは、コピーコンストラクタへのアクセスが禁止されているクラスをメンバとして持つクラスが上記の例にあたるようです。

コピーコンストラクタの引数が const 参照になっていないという事はありませんか?

const を付けていないと、コンテナへの代入時にコピーコンストラクタが定義されていないというエラーになります。(Visual C++ 6.0)

id:you1982

ご回答、ありがとうございます。ただ、それはないと思います。

・・・と思ったのですが、全クラスメンバについてコピーコンストラクタを確認してみたところ、引数にconstを付け忘れているメンバクラスを発見しました・・・。全くお恥ずかしい話です。ご指摘、ありがとうございました。

今回の教訓として、コピーコンストラクタの自動生成が行われない場合は、メンバ中にコピーコンストラクタが適切に定義されていない(privateになっていたり、引数定義にconstがついていなかったりする)ものがある、ということですね。

親クラスのコピーコンストラクタが不適切な場合は、アクセスできない旨のエラーが出るため分かりやすいですが、メンバの定義が不適切な場合は要注意です。トホホ・・・。

2007/02/18 18:05:02
  • id:you1982
    すみません、自己解決したっぽいです。

    ひとまず1例として見つけたのは、コピーコンストラクタへのアクセスが禁止されているクラスをメンバとして持つクラスが上記の例にあたるようです。
  • id:you1982
    うむむ、今、困っている問題は上記の例に当たらないような・・・。

    上のコメントに書いた例以外のケースがあれば教えてください。
  • id:kuro-yo
    ちょっと的をはずしているかもしれませんが、
    C++一般について、コピーコンストラクターって、自動生成されるものなんですか?
    コピーコンストラクターがなければ、byte-to-byteでオブジェクトがコピーされるだけですから、別にクラス毎に自動生成する必要はないですよね?
    C++Builder独自機能という事はないですか?
    ドキュメントにはエラーについての説明はありませんか?
  • id:you1982
    コピーコンストラクタの自動生成はBuilderに限った話ではなく、C++一般の話だと思います。また、必ずしもbyte-to-byteのコピーが実行されるわけでもないと思います。
  • id:kuro-yo
    >必ずしもbyte-to-byteのコピーが実行されるわけでもない
    すると、メンバ変数の中にコピーコンストラクターの定義されたクラスのインスタンスがある場合とかでしょうか。
  • id:you1982
    そうですね。
  • id:kuro-yo
    手許のコンパイラで実験してみました。
    (MetrowerksCodeWarriorPro7)
    なんと、エラーになるどころか、引数がconst参照でないコピーコンストラクターを作成でき、しかも、引数が参照するオブジェクトの内容を変更する事もできました。
    (もっとも、このコンパイラの仕様がおかしいのかもしれません)
  • id:Bookmarker
    >>
    親クラスのコピーコンストラクタが不適切な場合は、アクセスできない旨のエラーが出るため分かりやすいですが、メンバの定義が不適切な場合は要注意です。トホホ・・・。
    <<
    私は、Visual C++ と g++ しか使った事がないのですが、Visual C++(6.0) は分かり難い情報を出力する場合が少なくなく、g++ は Visual C++ よりは分かり易い情報を出力する場合が多いと感じています。

    今回の件では以下のような感じになりました。
    Visual C++ はコピーコンストラクタが定義されていないという情報しか出力してくれませんが、g++ はプラスαの情報『note: candidates are: Bar::Bar(Bar&)』があるので気が付く可能性がより高いと思います。

    % cat auto.cpp
    #include <vector>

    class Foo
    {
    public:
    Foo() {
    }

    // Foo(const Foo &) {
    Foo(Foo &) {
    }
    };

    class Bar
    {
    private:
    Foo foo_;
    };

    int main()
    {
    std::vector<Bar> container;
    Bar object;
    container.push_back(object);

    return 0;
    }

    % cl /W3 /GX /c auto.cpp
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
    Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

    auto.cpp
    C:\Program Files\Microsoft Visual Studio\VC98\include\xmemory(34) : error C2558: class 'Bar' : コピー コンストラクタが定義されていません。
    C:\Program Files\Microsoft Visual Studio\VC98\include\xmemory(66) : コンパイルされたクラスのテンプレートのインスタンス化 'void __cdecl std::_Construct(class Bar *,const class Bar &)' の参照を確認してください

    % g++ -Wall -c auto.cpp
    /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_construct.h: In function `void std::_Construct(_T1*, const _T2&) [with _T1 = Bar, _T2 = Bar]':
    /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_vector.h:560: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = Bar, _Alloc = std::allocator<Bar>]'
    auto.cpp:24: instantiated from here
    /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_construct.h:81: error: no matching function for call to `Bar::Bar(const Bar&)'
    auto.cpp:23: note: candidates are: Bar::Bar()
    auto.cpp:15: note: Bar::Bar(Bar&)
    /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/vector.tcc: In member function `void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = Bar, _Alloc = std::allocator<Bar>]':
    /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_vector.h:564: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = Bar, _Alloc = std::allocator<Bar>]'
    auto.cpp:24: instantiated from here
    /usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/vector.tcc:234: error: no matching function for call to `Bar::Bar(const Bar&)'
    auto.cpp:15: note: candidates are: Bar::Bar(Bar&)
  • id:you1982
    終了後にもかかわらず、いろいろと情報をありがとうございます。皆様、好きですね~(笑)

    >kuro-yo さん
    const引数を要求しない文脈からコピーコンストラクタが必要となったのであれば、おかしな動作とは言い切れない気もします。たとえば、Bookmarkerさんのようなコード例でコンパイルが通ってしまうということであれば、コンパイラの動作がおかしいというよりむしろSTL(vector)の実装の部分に問題がある、という可能性もありますよね。

    >Bookmarker さん
    Borland C++ Builder 5の挙動というかメッセージもVCよりはマシで上記のgccの方に近いですね。上記同様に、vectorからコピーコンストラクタが必要とされてエラーが出た場合の実際のメッセージ例はこんな感じです。

    [C++ エラー] memory.stl(193): E2285 'Bar::Bar(const Bar)' に一致するものが見つからない

    もっとダイレクトに、「Foo::Foo(const Foo)を定義しろ!」みたいに教えてくれると有り難いですよね。

    ただ、確かにgccなら「Bar::Bar(Bar&)」はあるけど「Bar::Bar(const Bar&)」がない、ということで、原因をconstに絞って考えられて良いですね。
  • id:kuro-yo
    確かに。で、Bookmarkerさんのコードを早速試してみました。
    Error:function call 'Bar(const Bar)' does not match
    'Bar::Bar()'
    'Bar::Bar(Bar&)'
    (instantiating:'std::allocator<Bar>::constructor(Bar*,const Bar&)')
    memory line 223 ::new((void*)p)T(val);
    …と、なりました。なるほど。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません