• ベストアンサー

C++ 共変 戻り値について

c++ で例えば、 class A { public:  virtual ~A() {} }; class B { public:  virtual ~B() {}  virtual B& hoge() = 0; }; class D:  public A,  public B { public:  virtual D& hoge() // この戻り値でエラー  {   ...;   return *this;  } }; のようにコードを書いたとします。 そして、これを gcc 3.3 でコンパイルしたところ「ゴメン。共変はサポートしてないんだ」って感じでエラーとなってしまいました(gcc 3.2 では問題を孕んでたくせに、何も言わずにコンパイル通ってました)。 この共変の意味や、その実際の挙動、問題点等は判ったのですが、では一体、この様な場合にどのようにコードを修正するのが正しいのか思い至りません。 どなたか、私ならって言うお考えがあればご教示願えないでしょうか? ちなみに、コンパイラーを変えるってのは無しです^^) ## gcc 4.0 ならいけるのかな??

noname#168973
noname#168973

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

どうしてもコンパイラを変えたくないなら、他の回答にあるように、返却値を B& にするしかないと思います。若干補足すると、フリー関数として、 inline B& hoge(B& b) {  return b.hoge(); } inline D& hoge(D& d) {  return static_cast<D&>(d.hoge()); } を作っておけば、多くの問題は解決するかと思います。 ちなみに、gcc-3.4.4ではこの不具合は解決していました。というわけで、gcc-4.0.0でも問題ないと思います。

noname#168973
質問者

お礼

ご回答ありがとうございます。 No.3 さんのお礼にも記述させてもらいましたが、今回の件は私の単なる勘違いでした。よって、実体化できない クラス B の リファレンスが派生クラスで戻せるなら、とりあえず問題は解決です。 ご提示頂いたアイデアは良いですね。C++ では、ついつい何でも method にしがちですが、クラスに属さない関数を利用したテクニックには私も最近気付き、色々と応用を考え始めたところでした。このアイデアも頂かせてもらいます ^^)

その他の回答 (3)

  • MASATO3
  • ベストアンサー率60% (27/45)
回答No.3

> 「ゴメン。共変はサポートしてないんだ」 > よって、その参照もエラーとなってしまいました。 エラーについてはエラーメッセージを正確に書いて下さい。 そうしないと、誤解が生じて質問者も回答者も損をします。 > 今回の件では class B は抽象クラスのため、実体化できません。 どこで実体化したのですか? 今まで提示されたコードはどこにもBを実体化するようなコードはありません。 > そうですね。でも、面倒というより、無ければ実際に困ってしまいます^^) covariantが使えないとプログラムが作れないというわけではないと思います。 コンパイラが変えられない以上、簡潔さを多少妥協することをお勧めします。

noname#168973
質問者

お礼

> エラーについてはエラーメッセージを正確に書いて下さい。 > そうしないと、誤解が生じて質問者も回答者も損をします。 すいません。手元にはそのコンパイラが無かったのと、今回の件はそのエラー自体を主題と思っていなかったため、適当に覚えていた内容を記述してしまいました。 > どこで実体化したのですか? > 今まで提示されたコードはどこにもBを実体化するようなコードはありません。 あれ!! 当方の勘違いだったようです。 ちょっと間抜けな事に別の部分で実体化できないと出ていたエラーを、この hoge() の部分と思い込んで、実体化できないクラスはその参照も返せないんだと勝手に思っていました。 実体化できないクラスでもその参照は返せるのですね。 と言う事はとりあえず問題解決です。 > covariantが使えないとプログラムが作れないというわけではないと思います。 > コンパイラが変えられない以上、簡潔さを多少妥協することをお勧めします。 確かに仰せのとおりですね。最悪は妥協も考えていましたが、これらのクラスを Template 等で meta programing 的に利用しようとすると、syntax は非常に重要となってしまいます。それで可能な限り突き詰めようと模索していました ^^)

  • MASATO3
  • ベストアンサー率60% (27/45)
回答No.2

class Dの関数hogeを virtual B& hoge() {  return *this; } と派生元と合わせ、hogeを呼び出しているほうでキャストしましょう。 D d; D& hoge = static_cast<D&>(d.hoge()); 面倒ですね。 面倒だからこんなキャストをしなくて良いように、covariant(共変?)という機能が追加されたのでしょう。

noname#168973
質問者

お礼

ご回答ありがとうございます。 そうですね。でも、面倒というより、無ければ実際に困ってしまいます^^) あまり、困っている人は居ないのかな??

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.1

ところで共変って何ですか? 今回の場合は、パラメータが一緒で戻り値の型のみが異なるhogeがオーバーロードされたことが原因のはずですが。 クラスBのhogeが純粋仮想関数になっているので、対応方法は クラスDのメソッドも B& hoge() にするのが正しいと思います。 で、使う側がdynamic_castでキャストをする感じかと。

noname#168973
質問者

お礼

ご回答ありがとうございます。 確かに dynamic_cast を利用するもの一つの方法かと思うのですが、今回の件では class B は抽象クラスのため、実体化できません。よって、その参照もエラーとなってしまいました。 じゃあ hoge() の戻り値を B のポインターにすればよさそうなものですが、そうすると下記の記述ができません。 ## 例えば、B に hoge() 以外に B& move(...), B& scaled(...), ## B& rotate(...) 等があったとき、 ## ## D d; ## d.move().scaled().rotate(); ### ポインターにすると、 ### d.move()->scaled()->rotate(); ### になりますが、ちょっと美しくありません。 で、共変の許されていない環境では、みなさん一般的にどうされているのかな? と思った次第です。 == ちなみに共変ですが、英語では covariant と表記されていました。 「C++ 共変」で ググッてみたところ、 http://www2s.biglobe.ne.jp/~ragnarok/program/cpp/vcnet.htm のようなページが見付かりました。

関連するQ&A

  • C++基底クラスに戻り値の異なる関数が宣言されている場合

    こんにちは。 質問させてください。 以下のようなコードがあったとします。 class Base1 { public:   virtual int get()=0; }; class Base2 { public:   virtual float get()=0; }; class Ex : public Base1, public Base2 { public:   int get(){return 0;}   float get(){return 0.0f;} }; void main() { } このプログラムをコンパイルすると 「'Ex::get': オーバーライドする仮想関数の戻り値の型が異なり、'Base1::get' の covariant ではありません。」 というエラーが出てしまいます。 関数の名前と引数が同じで戻り値だけが異なる場合はエラーが出るのはわかるのですが、基底クラスのBase1、Base2は変更不可能だとするとどのように回避すればいいのでしょうか? よろしくお願いします。 /*   WindowsXP Professional SP3   VisualStudio2005 AcademicEdition */

  • C++の継承の曖昧さの解決について教えてください

    こんにちは。 よろしくおねがいします。 class bass { public:   virtual ~bass(){} }; class Ex1 : public bass{}; class Ex2 : public bass{}; class Ex3 : public Ex1, public Ex2{}; void hoge( bass* foo ){} void main() {   hoge( &Ex1() );   hoge( &Ex2() );   hoge( &Ex3() ); } でhoge( &Ex3() );の関数呼び出しで曖昧さが解決できないとコンパイルエラーが出てしまいます。 これを解決するにはどのように指定すればよいのでしょうか?

  • c++のtemplateでエラーが出ます

    エラーメッセージ too few template-parameter-lists →私的には、templateへのパラメータが少なすぎる と理解しているのですが。。。 コード的にには、以下の通りです。 hoge.hh template<class T> myclass2 { public myclass1* piyo; : : } hoge.ccにて myclass1* myclass2<myclass3>:piyo; →ここで上記のエラー 別のヘッダにて class myclass3 : public myclass1<myclass3> { : : } という感じでコーディングしてます。 gcc3.2.2でのコンパイルでは、問題なくコンパイルでき 実行もできます。 ですが、gcc3.4.2を使用するとコンパイルエラーが出てしまいます。 このエラーの回避方法をご存知の方いらっしゃいましたら 御教授願えませんでしょうか よろしくお願い致します。

  • C++のtemplateクラス前方宣言について教えてください

    C++で、以下のようなコードを書いたのですがコンパイルが通りません。 どのようにコードを修正すればコンパイルを通すことができるでしょうか??教えてください。 template <class _T> class Test; // 上をclass Test<_T>にしてもコンパイルは通りません void main() {   Test<int> hoge; } template <class _T> class Test { public:   _T val;   Test():val(0){}; }; /*! エラー内容: 前方宣言class Test;の場合 'hoge'が未定義のclass'Test<_T>'で使用しています。   with   {     _T=int   } 前方宣言class Test<_T>;の場合 構文エラー:';'が'<'の前にありません。 構文エラー:'<' 'hoge'が未定義のclass 'Test<_T>'で使用しています。 */ 開発環境: VisualStudio2005 AcademicEdition WindowsXP Professional メモリ:2048MB CPU:Core2Duo 1.33GHz よろしくお願いします。

  • (C++)スマートポインタをメンバ変数で使いたい

    Viual Studio 2013を使ってC++のコードを書いています。 以下のコードで、new-deleteの クラス生成をスマートポインタで置き換えたいのですが、 うまくいきません。具体的には/* not smart */の部分を スマートポインタで置き換えたいが、C2059のエラーが出てきて コンパイルできないのが理解できていません。 どなたかわかる方教えていただければ幸いです。 よろしくお願いします。 // ----コードは以下---- #include <memory> #include <iostream> class Hoge { public: Hoge(){ std::cout << "constructed!" << std::endl; }; ~Hoge(){ std::cout << "destructed!" << std::endl; }; private: }; class Hogehoge { public: Hogehoge(){}; ~Hogehoge(){ delete test; /* not smart */}; private: // error C2059 w/ VS2013 // std::unique_ptr<Hoge> test(new Hoge); Hoge* test = new Hoge; /* not smart */ }; void main() { Hogehoge foo; }

  • 【C言語】配列の中に配列を入れたい

    const double A[2] = {1.0, 2.0}; double B[2][3] = { { A[0], A[1], 0.0 }, { 0.0, 0.0, 0.0 }}; このようなプログラムをコンパイルしようとすると、 初期化子の要素が定数ではありません というエラーが出てしまい困っています。どのように解決したらいいでしょうか。 OSはlinux、コンパイラgccです。

  • 戻り値の異なるメソッドの多重定義はできないのですか?

     戻り値の異なるメソッドの多重定義はできないものでしょうか?  例えば、Integer型の戻り値を持つメソッドHogeを持つクラスClass1があるとします。 public Class1{ ・・・  public Integer Hoge(){ ・・・ }     ・・・ } これを継承したクラスClass2を作ります。 public Class2 extend Class1{ ・・・ public int Hoge(){ Integer buf = this.Hoge(); int buf2 = buf.intvalue(); }   ・・・ }  私の環境ではエラーとなって実行できませんでしたが、 こういったことはできないものなのでしょうか?  やりたいことは、(Javaのライブラリに含まれている)Integer型を持つメソッドを他のクラスから使用できるようにするためにint型に変換することなんですが、他のクラスの変更は一切しないで、この部分を書き換える(アダプタパターンを使う)ことによって実現したいと考えています。  他に良い方法はあるでしょうか?宜しければ、アドバイスお願いします。  

    • ベストアンサー
    • Java
  • static constメンバ変数(配列)の初期化について

    C++初心者です。 constメンバ変数の初期化について教えてください。 クラスの中に、static constメンバ変数(配列)を持ちたいのですが、 <コード1> class hoge { public : hoge(){}; virtual ~hoge(){}; static const int fuga[2] = {1, 2}; }; とすると、VC++ 2005では、 error C2059: 構文エラー : '{' error C2334: '{' の前に予期しないトークンがありました。関数の本体は無視されます というエラーが発生します。 何がいけないのでしょうか? また、下の様にするとOKでした。 <コード2> class hoge { public : hoge(){}; virtual ~hoge(){}; static const int fuga[2]; }; const int hoge::fuga[2] = {1,2}; こうすればコンパイルが通る事は分かったのですが、なぜこんな面倒な事をしないといけないのかが分かりません。 コード1では何がいけないのでしょうか? 以上、よろしくお願いします。

  • C++のクラスについて

    /*以下のコメントがある行では何故、コンストラクタ(class2::class2)を指定出来ないのですか? デストラクタ(class2::~class2)の場合も問題なくコンパイルが通り、実行できます (http://codepad.org/1oJkxjyZ の23行目) 開発環境 Windows XP SP3 コンパイラ:GCC 実行結果 class1のコンストラクタ class2のコンストラクタ aiueoの実行 class2のデストラクタ class1のデストラクタ */ #include<iostream> class class1; class class2; class class1{ public: class1(); ~class1(); private: class2*pointer; }; class class2{ public: class2(); ~class2(); void aiueo(); }; class1::class1(){ std::cout<<"class1のコンストラクタ"<<std::endl; pointer=new class2(); pointer->aiueo(); //aiueoを~class2に置き換えてもコンパイル出来るが、class2だとエラーが出る } class1::~class1(){ delete pointer; std::cout<<"class1のデストラクタ"<<std::endl; } class2::class2(){ std::cout<<"class2のコンストラクタ"<<std::endl; } class2::~class2(){ std::cout<<"class2のデストラクタ"<<std::endl; } void class2::aiueo(){ std::cout<<"aiueoの実行"<<std::endl; } int main(){ class1 test1; return 0; }

  • C++ のクラスの定義

    C++の初心者です。 C++のことでお聞きしたいことがあります。 namespace test1{ class A { friend class B; protected: int a; }; } とtest1の下にAクラスを作り namespace test2{ class B { public: test1::A A1; }; } と別のnamespaceにBというクラスを作り、 A1というAのインスタンスを持ちたいのです。 しかし、Aというクラスを先に宣言しているので Bというクラスが解らないらしく friendがうまく働きません。 friend class test2::B としても test2がまだ宣言されていないので、 ????とコンパイルエラーが出ます。 先にtest2::B を宣言してしまうと、 今度はtest1::Aが解らずに エラーが出てしまいます。 こんな場合はどのように書けばよろしいのでしょうか? 環境はwindows2000でVC++ 6.0です。 どうか宜しくお願いいたします。

専門家に質問してみよう