none
C1001の解決方法について RRS feed

  • 質問

  • こんにちは。
    もうすぐお正月なのですが、質問をさせてください。

    VC++8.0で以下のソースをコンパイルするとC1001エラーが出てしまいます。ヘルプを見ると、コンパイルオプションを変更すると解決すると言ったようなことを書いていたのでいろいろと試しているのですがうまくいきません。

    VC++6.0や7.1ではコンパイルできるので、オプション設定で何とかできるのではないかと思うのですが、どうすればコンパイルできるでしょうか?

    ~~~環境~~~
    Windows XP Professional
    Visual Studio 2005 Professional (SP1適応済み)
    (Expressでも同じようです)

    Win32のスタティックライブラリプロジェクトを作成してビルド

    ~~~以下ソース~~~

    class __declspec(dllexport) ClassA 
    {
    public:
     ClassA(){};
    private:
     ClassA(const ClassA& object){};
    public:
     virtual ~ClassA(){};
    };

    class __declspec(dllexport) ClassB : public ClassA
    {
    public:
     ClassB(){};
     virtual ~ClassB(){};
    };

    class __declspec(dllexport) ClassC : public ClassB
    {
    public:
     ClassC(){};
     ClassC(const ClassB& obj){};
     virtual ~ClassC(){};
    public:
     ClassC func1(){
      ClassC cl1;
      return cl1;
     }
     ClassC func2(){
      return func1();   // C1001
     }
     void func3(){
      ClassC cl2;
      ClassC cl3 = cl2;  // C1001(ダイアログ付き)
     }
    };

    ~~~~~~~
    よろしくお願いします。

    2006年12月27日 1:47

回答

  •  rint さんからの引用

    VC8.0の場合、ClassC(const ClassB& obj)という宣言はコピーコンストラクタとして認識されないということなのでしょうか?(そもそもコピーコンストラクタではない?)

    そもそもコピーコンストラクタとは言えないでしょう。

    T( const T& other );

    の形式がコピーコンストラクタで、それ以外は単なる引数付きのコンストラクタです。

    もし、コピーコンストラクタを使わずに複製は明示的に、というのであれば、T::Copy() などといったメソッドを持たせたほうが良いと思います。

    2006年12月28日 20:02
  • 誤解させてしまったようで申し訳ありません。

    私は class ClassC の copy constructor をきちんと定義した方が良いと思います。

    すなわち、ClassC (const ClassB& objB) は破棄(他に目的があるのでしたら残してもかわないと思いますが) して ClassC (const ClassC& objC) を定義です。

     

    2007年1月9日 5:00

すべての返信

  • 良く分からないのですが、

    private:
     ClassA(const ClassA& object){};

    と、class ClassA の copy constructor を private にしているのは何故なのでしょう?

    というのは、「__declspec(dllexport)」を全てコメントアウトしてみると分かるのですが、

    return func1();   // C1001

    ClassC cl3 = cl2;  // C1001(ダイアログ付き)

    のあたりで、インスタンスをコピーしようとして class ClassA の copy constructor が呼び出されようとしているのですが、private 宣言のために compile error が発生します。

    private 宣言を外せば、「__declspec(dllexport)」が有効なままでも C1001 エラーは発生しないようです。

     

    2006年12月28日 7:00
  • 返信ありがとうございます。

    privateは単純にClassAのコピーを作らせないためです。サンプルのクラスだとまったく意味はないのですが、実際に使用しているクラスだとこのあたりが必要になってしまっていて残念ながらはずせません。

    ご指摘いただいたように[__declspec(dllexport)]をはずしてビルドしてみたところ、ClassBのコピーコンストラクタを作成しました、というメッセージが出ました。
    ためしにClassBに以下のようなコピーコンストラクタを追加すると[__declspec(dllexport)]をつけていてもエラーは出ませんでした。
      ClassB(const ClassB& object)

    この場合、ClassCのコピーを行うときに、ClassC(const ClassB& obj)が呼ばれず、ClassB(const ClassB& object)が呼ばれるようです。

    VC7.1の場合、ClassB(const ClassB& object)が定義されていても、ClassC(const ClassB& obj)が呼ばれていたようなのですが、VC8.0の場合、ClassC(const ClassB& obj)という宣言はコピーコンストラクタとして認識されないということなのでしょうか?(そもそもコピーコンストラクタではない?)

    2006年12月28日 16:12
  •  rint さんからの引用

    VC8.0の場合、ClassC(const ClassB& obj)という宣言はコピーコンストラクタとして認識されないということなのでしょうか?(そもそもコピーコンストラクタではない?)

    そもそもコピーコンストラクタとは言えないでしょう。

    T( const T& other );

    の形式がコピーコンストラクタで、それ以外は単なる引数付きのコンストラクタです。

    もし、コピーコンストラクタを使わずに複製は明示的に、というのであれば、T::Copy() などといったメソッドを持たせたほうが良いと思います。

    2006年12月28日 20:02
  • 既にRAPTさんがご指摘下さっていますが、

    ClassC(const ClassB& obj)

    は copy constructor ではありません。恐らくは type miss なのでしょうけれど、

    ClassC(const ClassC& obj)

    です。ですので、

    ためしにClassBに以下のようなコピーコンストラクタを追加すると[__declspec(dllexport)]をつけていてもエラーは出ませんでした。
      ClassB(const ClassB& object)

    この場合、ClassCのコピーを行うときに、ClassC(const ClassB& obj)が呼ばれず、ClassB(const ClassB& object)が呼ばれるようです。

    class ClassC のインスタンスのコピーを行う時に、class ClassC の default の copy constructor が呼び出されてしまって、class ClassC は class ClassB から派生していますから class ClassB の定義している部分を copy しようとして class ClassB の copy constructor が呼び出されているのです。

     # この場合、class ClassB の copy constructor が定義されていますから、class ClassA までは行かずに止まります。ので、compile error は出なかったのです。

     

     

    2006年12月29日 3:14
  • 返信ありがとうございます。

    なるほど引数つきのコンストラクタなのですね。
    そう考えるとコピーするときにこの関数が呼ばれないことも理解できます。

    ただそうした場合、VC7.1などのバージョンでエラーにならなかった原因が気になります。
    単純にVC8.0になってチェックが厳しくなっただけなのでしょうか?

    2007年1月3日 8:04
  • VC7.1でチェックしてみました…。が、そのままのコードではなくて少しいじっています。

    class ClassA {
    public:
     ClassA(){
      std::cout << "ClassA::constructor" << std::endl ;
     };
    private:
     ClassA(const ClassA& object){
      std::cout << "ClassA::copy constructor" << std::endl ;
     };
    public:
     virtual ~ClassA(){
      std::cout << "ClassA::destructor" << std::endl ;
     };
    };

    class ClassB : public ClassA
    {
    public:
     ClassB(){
      std::cout << "ClassB::constructor" << std::endl ;
     };
     virtual ~ClassB(){
      std::cout << "ClassB::destructor" << std::endl ;
     };
    };

    class ClassC : public ClassB {
    private:
     int  m_iValueC ;
    public:
     ClassC() : m_iValueC (0) {
      std::cout << "ClassC::constructor" << std::endl ;
     };
     ClassC(int iValue) : m_iValueC (iValue) {
      std::cout << "ClassC::constructor" << std::endl ;
     };
     ClassC(const ClassB& obj){
      std::cout << "ClassC::constructor??" << std::endl ;
     };
     virtual ~ClassC(){
      std::cout << "ClassC::destructor" << std::endl ;
     };
    public:
     ClassC func1(){
      ClassC cl1;
      return cl1;
     }
     ClassC func2(){
      return func1();   // C1001
     }
     void func3(){
      std::cout << "ClassC::func3() before: ClassC cl2;" << std::endl ;
      ClassC cl2 (100) ;

      std::cout << "ClassC::func3() before: ClassC cl3 = cl2;" << std::endl ;
      ClassC cl3 = cl2;  // C1001(ダイアログ付き)
      std::cout << "ClassC::func3() after: ClassC cl3 = cl2;" << std::endl ;
     }
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
     ClassC theC ;

     theC.func3 () ;
     return 0;
    }

    標準出力の結果を追い掛けると(途中だけですが)、

    ClassC::func3() before: ClassC cl3 = cl2;
    ClassA::constructor
    ClassB::constructor
    ClassC::constructor?

    cl3 の定義時に = cl2 があるので、対応した constructor を呼び出そうとして、都合良く ClassC (const ClassB& obj) が呼ばれているようです。 ClassC である cl2 を ClassB で受けて初期化しているようですね。

    # gcc4.x で禁止された cast-as-lvalue みたいなものなのでしょうか。C++ の規格が手元にないので何とも言えませんが。

    いずれにせよ ClassB で受けてしまっているので、cl2 の m_iValueC の値は cl3 に反映できなくなってしまうわけですが…そこはかまわないのでしょうか?

     

    2007年1月5日 11:04
  • 返信ありがとうございます。

     Takashi SAKAMOTO さんからの引用

    いずれにせよ ClassB で受けてしまっているので、cl2 の m_iValueC の値は cl3 に反映できなくなってしまうわけですが…そこはかまわないのでしょうか?

    ClassCには変数は無いので問題はありません。

     Takashi SAKAMOTO さんからの引用

    cl3 の定義時に = cl2 があるので、対応した constructor を呼び出そうとして、都合良く ClassC (const ClassB& obj) が呼ばれているようです。 ClassC である cl2 を ClassB で受けて初期化しているようですね。

     この動作なのですが、以下のような記述でよいでしょうか?

      ClassC  cl2;
     ClassC  cl3 = *( (ClassB*) (&cl2) );

    2007年1月9日 4:16
  • 誤解させてしまったようで申し訳ありません。

    私は class ClassC の copy constructor をきちんと定義した方が良いと思います。

    すなわち、ClassC (const ClassB& objB) は破棄(他に目的があるのでしたら残してもかわないと思いますが) して ClassC (const ClassC& objC) を定義です。

     

    2007年1月9日 5:00
  • 返信ありがとうございます。

    確かにやっていることはコピーなので、きちんとしたコピーコンストラクタを定義した方がよさそうですね。

    参考になりました。ありがとうございました。

    2007年1月9日 5:52