• ベストアンサー

C++テンプレートクラスの内部クラスについて

テンプレートクラスについていろいろ試していたところ以下のようなコードで struct A {     struct AA { };     operator A::AA() { return A::AA(); } // (1) }; template<typename T> struct B {     struct BB { };     template<typename U>     operator B<U>() { return B<U>(); } // (2)     template<typename U>     operator typename B<U>::BB() { return typename B<U>::BB(); } // (3) }; int main() {     static_cast<A::AA>(A()); // (1) ok     static_cast<B<int> >(B<short>()); // (2) ok     static_cast<B<int>::BB>(B<short>()); // (3) compile error     return 0; } (1)と(2)はできて(3)だけがコンパイルを通りませんでした。 試したコンパイラはVC9とg++(3.3.4)とbcc32(5.5.1)で、VC9では以下のようなエラーをはきました。 「error C2440: 'static_cast' : 'B<T>' から 'B<T>::BB' に変換できません。     with [ T=short ] and [ T=int ] コンストラクタはソース型を持てません、またはコンストラクタのオーバーロードの解決があいまいです。」 (1)と(2)ができれば(3)のようなこともできそうな感じがしたのですが、他に書き方があるのでしょうか。 どなたかご存知の方がいらっしゃいましたらご教示お願いします。

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

  • ベストアンサー
回答No.3

 こんにちは。何度もすんません。  struct BBの中にダミーのenumを置いて、operator U()の中に其れを書けば、B<U>::BB以外のキャストをした時にコンパイルエラーを出させる事が出来ました。  此れにて筆を収めさせて頂きます。 struct B { struct BB{ enum{IsBB = 0}; }; //B<U>への変換を受け入れる template<typename U> operator B<U>(){ return B<U>(); } //(2) //以下でB<?>::BBである事を保障出来る template<typename U> operator typename U()//(3) { U::IsBB;//UがBBで無い時、ココでコンパイルエラーに出来る return typename U(); } //boolへの変換を受け入れる operator bool() const { return true; } //(4) }; static void test(const B<int>::BB&) { } int main() { static_cast<A::AA>(A());//(1) ok static_cast<B<int> >(B<short>());//(2) ok static_cast<B<int>::BB>(B<short>());//(3) ok static_cast<B<int>::BB>(B<int>());//(3) ok ::test(B<short>());//(3) ok ::test(B<int>());//(3) ok //以下の3つのキャストは認めてはいけない static_cast<int>(B<int>()); //(3) error static_cast<A>(B<char>()); //(3) error static_cast<A::AA>(B<long>()); //(3) error if(B<char>())//(4) ok { // } return 0; }

vapraip
質問者

お礼

御礼が大変遅くなってしまいすみません。 > 何度もすんません。 だなんてとんでもございません。とてもありがたい限りです。 逆に私の方が申し訳ない限りです・・・ なるほど、ちょっと強引かも知れませんがお答えいただいた方法で目的としていた機能は 実現できました。 感激です。ありがとうございます。 ・・・でもやっぱり(1)(2)は適合してくれて、なぜ(3)だけ適合してくれないのかが 気になるので質問内容を改めて再投稿してみます。 これまでの非常に丁寧なご回答大変ありがとうございました。 またお付き合いいただけるとうれしいです。

その他の回答 (3)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

GCC だと「コピーコンストラクタとデフォルトコンストラクタのどっちを使うかわからん」という素敵なエラーなんですよね.... ちょっとこの辺は規格でも探し切れていません. とりあえず手元の GCC で確認した限りでは ・B<U>::BB に, B<T> を引数にするコンストラクタを作る ・B<T> で B<U>::BB をフレンド宣言する で逃げられるかもしれない.

vapraip
質問者

お礼

御礼が遅くなってしまい申し訳ございません。 ご回答ありがとうございます。 > B<U>::BB に, B<T> を引数にするコンストラクタを作る は、おそらく最初に machongola さんにご回答いただいたものだと思うのですが、 違っていたらすみません。あと私の質問内容が悪くてすみません。 もう一度質問内容を改めて再投稿したいと思います。 せっかく回答していただきながら大変申し訳ございません。

vapraip
質問者

補足

正確にはキャスト自体が目的ではなく(1)のような内部クラスへの型変換演算子と (2)のような他の引き数を持つテンプレートクラスへの型変換演算子は適合するのに、 その2つを組み合わせた(3)の他の引き数を持つテンプレートクラスの内部クラスへの 型変換演算子は適合しないということがどうにも納得がいかなかったのです。 ちなみに最初のものには書いていませんでしたが template<class T> struct B {     struct BB { };     operator typename B<T>::BB() { return typename B<T>::BB(); } // (4) }; の(4)ように同じ引き数を持つテンプレートクラスの内部クラスへの型変換演算子は 適合してくれました。 なのでどういうルールで適合してくれるのか、わからなくなってしまったのです。

回答No.2

 こんにちは。御礼頂きました。  其の後、チョッと試したのですが、operator B<U>::BB()とせず、ストレートにoperator U()とすれば適合する様です。  尚(2)(4)(5)が無いと全て(3)が使用されます。要は、operator U()だと何でもかんでも適合してしまいます。  なので、オーバーロードで型の特定を厳密化させて行き、なるべくoperator U()に適合するのを遅延させた方が良さそうです。  後、最後の最後までoperator B<U>::BB()が適合する事はありませんでした。因みにテンプレートx2パラメータを以ってしても不可能でした。  ↓コレでも無理  template<template<class>class X, typename U>  operator typename X<U>::BB() { return typename X<U>::BB(); } //一応出来たので参考程度に template<typename T> struct B { struct BB{}; template<typename U> operator B<U>(){ return B<U>(); } // (2) template<typename U> operator typename U(){ return typename U(); }// (3) operator typename B<T>::BB() { return typename B<T>::BB(); }// (4) operator bool() const { return true; } // (5) }; static void test(const B<int>::BB&) { } int main() { static_cast<A::AA>(A());//(1) ok static_cast<B<int> >(B<short>());//(2) ok static_cast<B<int>::BB>(B<short>());//(3) ok static_cast<B<int>::BB>(B<int>());//(4) ok ::test(B<short>());//(3) ok ::test(B<int>());//(4) ok if(B<char>())//(5) ok { // } return 0; }

vapraip
質問者

お礼

非常に丁寧なご回答ありがとうございます。

回答No.1

 こんばんは。  (3)には適合しない見たいです。  BBにB<T>の変換コンストラクタを書けばまかり通るようです。でも、何だか怪しい様な・・・。 template<typename T> struct B { struct BB { explicit BB(const B<T>& r){ } void TestMethod(){ std::cout<< "test method" << std::endl; } }; template<typename U> operator B<U>(){ return B<U>(); } // (2) //ココは適合しないらしい //template<typename U> //operator typename B<U>::BB(){ typename return B<U>::BB(); } // (3) }; int main() { static_cast<A::AA>(A());// (1) ok static_cast<B<int> >(B<short>());// (2) ok static_cast<B<int>::BB>(B<short>());// (3) ok static_cast<B<int>::BB>(B<short>()).TestMethod();// (4) ok return 0; }

vapraip
質問者

お礼

こんばんは。返事が遅くなりましてすみません。 なるほど内部クラスに変換コンストラクタを書いてあげればいいということですね。 ですがやはり(3)のように型変換演算子のオーバーロードで何とかすることは できないでしょうか。 というのも STL の auto_ptr の auto_ptr_ref のようなものを内部クラスにして 書くようなことはで出来ないものかと考えていたのです... せっかくお答えいただいたのに私の説明不足で申し訳ございません。 ですが型変換は出来ることがわかったので大変助かりました。 ありがとうございます。

関連するQ&A

  • テンプレート仮想関数のようなもの

    仮想関数をテンプレートにできないのは仕様と理解しているのですが、多少複雑になっても、それぞれの型に対するメンバ関数をいちいち記述していくことを回避するようなテクニックはありませんか? struct HOGE{ //template<typename T> //virtual operator T() = 0; //上記ができないので以下を全て記入していかなければならない virtual operator int() = 0; virtual operator short() = 0; //... }; struct HOGE1 : HOGE{ //template<typename T> //operator T() overtride{return T();} //上記ができないので以下を全て記入していかなければならない operator int(){return 0;} operator short(){return 0;} //... };

  • テンプレートクラス中のフレンドクラス

    下記をg++(fedora core1)でコンパイルしたところ、 #include <iostream> using namespace std; template< typename T > class A {   T a; public:   A(T aa ) : a(aa) { }   friend ostream& operator<<( ostream &os, const A &a ); //9行目 }; template< typename T > ostream& operator<<( ostream &os, const A<T> &a ) { return os << a.a; } int main( ) {   A<int>  a(5);   cout << a << '\n';   return 0; } 9行目にこのような警告・エラーが出てコンパイルできませんでした。(下記のオプションも試してみましたがダメでした) friend declaration 'std::ostream& operator<<(std::ostream&, const A<T>&)' declares a non-template function (if this is not what you intended, make sure the function template has already been declared and add<> after the function name here) -Wno-non-template-friend disables this warning. :undefined reference to 'operator<<(std::basic_ostream <char, std::char_traits<char> >&, A<int> const&)' なぜ、コンパイルできないのかが分かりません。ちなみに、bcc32(borland c++ compiler5.5.1)では同様のエラーが出てコンパイルできず、cl(VC++6.0)ではコンパイル・実行可能でした。 ご存知の方いらっしゃったらご教授お願いします。(bccとclはWinXPです)

  • テンプレートクラス内のテンプレートクラス(インナークラス)のメソッドを実装ファイルで定義したい

    現在、ヘッダファイル内で下記のようなクラスを宣言・定義しています。 // test.h template < typename T1 > struct A {   template < typename T2 > struct B   {     B( A const& arg ) { ... }   }; }; テンプレートクラスが入れ子になっていて、Bのコンストラクタが引数としてAを取っています。 しかし現状ではコンパイルがとんでもなく遅くなってしまうので、 Bのコンストラクタは宣言のみとし、別途実装ファイル(test.cpp)に定義を書きたいと思っています。 ところがメソッドのシグネチャをどう書けばよいのか分からなくなってしまいました。 苦し紛れに // test.cpp template < typename T1, typename T2 > A<T1>::B<T2>::B( A<T1> const& arg ) { ... } などと書いてみましたが、違うようです。 解決方法はありますでしょうか? 環境はVC7.1かVC8でコンパイルできればよいです。 よろしくお願いいたします。

  • テンプレートが複雑すぎる?

    以下のような文字列比較関数を作ろうと思っています。 ○配列変数であるときは配列版を、そうでないときはポインタ版を呼び出す  配列のサイズがわかるときは、仮に\0で終端していなくてもそれ以上検索しない ○charかwchar_tかを意識せずに使える ○charとwchar_tという記述ではなく、templateでできるだけジェネリック?的に記述したい //charを入れるとwchar_t、wchar_tを入れるとcharを返すメタ関数 template<typename T> struct invert{}; template<> struct invert<char>{ typedef wchar_t t; }; template<> struct invert<wchar_t>{ typedef char t; }; //同じ型同士の比較は省略 //A: 両方ポインタ template<typename T> bool compare(const T *const &v1, const typename invert<T>::t *const &v2); //B1: 左がポインタ、右が配列 template<typename T, size_t L> bool compare(const T *const &v1, const typename invert<T>::t (&v2)[L]); //B2: Tとtypename invert<T>::tを逆に template<typename T, size_t L> bool compare(const typename invert<T>::t *const &v1, const T (&v2)[L]); //C: 左が配列、右がポインタ //省略 //D: 両方配列 template<typename T, size_t L1, size_t L2> bool compare(const T (&v1)[L1], const typename invert<T>::t (&v2)[L2]); char aa[] = "abc"; char *pa = aa; wchar_t aw[] = L"abc"; wchar_t *pw = aw; bool c1 = compare(pa, pw); //A bool c2 = compare(pa, aw); //B1はC2784, B2なら可 bool c3 = compare(aa, pw); //C bool c4 = compare(aa, aw); //DはC2784 invert<T>::t (&v2)[L]という記述が複雑?なのかB1は呼べず、B2と記述すると通りました。 しかしDはどちらも配列であるため、B2のように回避できません。 オーバーロードを全て記述せずに、できるだけ簡単にすます方法はありませんか? 環境はVC++2010です。

  • C++ template operator T()

    ソースを見ていて分からないところがあったので教えて下さい。 なんと説明すればいいのか分からないので下に要約したソースを書きます。 template <typename T> class Hoge { T a_; public: Hoge(T a = 0) : a_(a) {} operator T() const { // ココが分からない return a_; } }; 最初は、関数オブジェクトかと思ったのですがそうではないですよね? operator()(引数) ですよね? 次は、Tのコンストラクタかと思ったのですが、class Hoge の中に書くのも変な気がしました。

  • C++テンプレートでの引数

     こんにちは。 今回は、C++ テンプレートで引数に構造体を使えないか悩んでいます。 例えば #include <stdio.h> typedef struct _rect{ int x; int y; }RECT; typedef struct _rect{ double x; double y; }DATA; template <calss T> T Function(T abc) { // 構造体の要素にも対応している。 cout << abc//の要素x << endl; cout << abc//の要素y << endl; return T; } int main() { RECT t; DATA r; t.x = 80; t.y = 90; t = Function(t); // こういった事をしたい r = Function(r); return 0; } というふうにしたいんです。 テンプレート関数に引数として構造体にしてそれぞれの要素を参照する場合はどうすれば よろしいのか教えてください。 よろしくお願いします。

  • テンプレートクラスでのクラス変数

    テンプレートから生成したクラスごとにクラス変数を設けて、 かつ、クラス変数の初期化をいちいちやらなくてもよい という状況を作りたいと考えています。 そこでたとえば template <int N> class test { public: static int class_var() { static int class_var_ = N; return class_var_; } }; としたとき test<1>::class_var() と test<2>::class_var() はどのような値になるのでしょうか? VC++ではテンプレート全体で class_var_の値が同じになってしまうようのなのですが。。。 そもそも、こういうクラス変数自体を つくろうという考えが間違っているのでしょうか? どなたかご存知でしたらお教えください。

  • c++11での文字列リテラルの特殊化について

    c++11言語でのテンプレート部分特殊化についての質問です。 コメントアウト部分は出力結果です template<class T> struct VT { static const int type = 1;}; template<class T,int N> struct VT< T[N] > { static const int type = 2;}; template<class T,int N> struct VT< const T[N] > { static const int type = 3;}; template<class T> struct VT< T* > { static const int type = 4;}; template<class T> struct VT< const T*const > { static const int type = 5;}; #include<iostream> #include<typeinfo> int main(){ std::cout<<"A:"<< VT< char >::type << std::endl; // A:1 std::cout<<"B:"<< VT< char[10] >::type << std::endl; // B:2 std::cout<<"C:"<< VT< char* >::type << std::endl; // C:4 std::cout<<"D:"<< VT< char const [1] >::type << std::endl; // D:3 std::cout<<"E:"<< VT< decltype("") >::type << std::endl; // E:1 std::cout<<"G:"<< typeid( char const [1] ).name() << std::endl;// G:char const [1] std::cout<<"H:"<< typeid( "" ).name() << std::endl;// H:char const [1] } 型名を直接記述したD,G、文字列リテラルを記述したE,H。 コンパイラ毎の差はあれど、GとHの型名は同じものが表示されます。 ですが、[D:3] [E:1]と値は違い、別の特殊化テンプレートが使われています。 この部分が分かりません。 また、配列リテラル、文字列リテラルに対し部分特殊化テンプレートを宣言する方法などありましたら、ご教示お願いします。

  • templateのmap処理でエラー

    C++のTemplateを勉強中のものです。 以下の処理でエラーが発生します。(ソースは一番下です) コンパイラはgcc4.6.6です。 【やりたいこと】 ・Parentクラス内でChildクラスを管理したい ・ParentクラスはSingleton扱いにしたい  (※下のソースではシンプルにするためにあえて実装していません) ・Parent内でChildクラスのポインタとIDをSTLのmapを使って保持したい 【エラー内容】 ・発生箇所:m_Map.insertの行 ・エラー内容は「undefined reference to `Parent<Child>::m_Map'」 ・m_Mapをstaticにしなければ出ない  ※上記でも述べましたが、Singletonとして扱いたいので、一元管理するmapはstaticとしたいです。 で、質問なのですが ・staticの場合に出る理由 ・上記エラーの解消方法 です。 お詳しい方いましたらご教示いただけませんでしょうか? ========================== #include <iostream> #include <map> #include <cstdio> using namespace std; class Child { }; template <typename T> class Parent { private: T *m_pTemp; // ここのstaticをはずすとエラーが出ない static map<int, T*> m_Map; public: T* createChild(); }; template <typename T> T* Parent<T>::createChild() { m_pTemp = new T; // ここでエラー発生。 m_Map.insert(typename map<int, T*>::value_type(1,m_pTemp)); return m_pTemp; } int main() { Parent<Child>* pParent = new Parent<Child>(); Child* c = pParent->createChild(); printf("c:%x", c); return 0; } ==========================

  • c++におけるtemplateについて

    c++の自作templateを関数宣言部と関数定義部とで、ファイルを分割する事を考えているのですが、エラーが出てtemplateをincludeすることが出来ないでいます。 例えば、 <ファイル:test.H> #ifndef test_H #define test_H #include <iostream> template<class T> class test { private: int row_, col_; public: test( int , int ); }; #endif <ファイル test.C> #include "test.H" template<class T> test<T>::test(int a, int b) :row_(a), col_(b) { std::cout << "コンストラクター" << std::endl; } <ファイル main.C> #include "test.H" #include<iostream> int main() { test a( 3, 3) return 0; } のようにtemplateファイルを分割した場合、 undefined reference to `test<double>::test(int, int)' collect2: ld returned 1 exit status のようなエラーがでます。 書籍:新c++言語入門・シニア編(下)の29章には「export」を関数定義部に付ければ良いような記述がありましたが、エラーを解決することが出来ませんでした。 main.Cにて、test.Cをインクルードするとこの問題を回避することが出来るのですが・・・。 そもそもtemplateは上記のように宣言部と定義部とを別々のファイルにすることはできないのでしょうか?

専門家に質問してみよう