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

このQ&Aのポイント
  • C++のテンプレートを使用して、引数に構造体を含める方法についてご質問いただきました。
  • 質問文章では、構造体を含んだテンプレート関数を使用して、構造体の要素を参照する方法についての具体的な例が示されています。
  • この方法を使って、C++のテンプレートを活用しながら構造体の要素を引数として受け取ることができます。
回答を見る
  • ベストアンサー

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; } というふうにしたいんです。 テンプレート関数に引数として構造体にしてそれぞれの要素を参照する場合はどうすれば よろしいのか教えてください。 よろしくお願いします。

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

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

>構造体のメンバ名は統一しないとダメみたいです。 はい、整合性のチェックというのはそれも含んだ意味です。 というより統一した構造体を複数作らないといけないなら 繰り返しになりますが、最初っから #include <stdio.h> template < class T > struct RECTEX { T x; T y; void Function() const { cout << x << endl; cout << y << endl; } }; int main(void){ RECTEX<int> t = {80,90}; RECTEX<double> r = {1.0,2.0}; t.Function(); r.Function(); return 0; } こういったソースにしてしまうのが最善解に思います。

DEADSPACE566
質問者

お礼

回答ありがとうございます。 構造体をテンプレート化してしまった方が扱いやすいということですね。

その他の回答 (7)

回答No.8

ここまでの文章から勘を働かせると おそらくCからC++に移行したばかりでご存知なかったのでは という気がしてきましたので、一応 C++ではstruct にも class にも 変数だけでなく関数も入れておくことができます。 struct A { int i; void IncrementI(){ ++i; } }; この、IncrementIをメンバ関数と呼びます。 A a; a.i = 7; //a.iは7 a.IncrementI(); //++a.iと同じ結果になり、a.iは処理後8に 「アクセス修飾子」とこの「メンバ関数」を組み合わせることによって 生のC言語と比べ、絶大に読みやすい(或いは保守・拡張が遥かに楽な) コードを作っていくことができます。 struct のデフォルトのアクセス修飾子はpublic class のデフォルトのアクセス修飾子はprivate になっていて、privateというのは、外部から直に触ることが出来ないということになっています。 class A { int i; //int型のメンバ変数をprivateで一つ持つ。 public: //この下の関数はpublicになります。 //クラス名(); は、「コンストラクタ」という初期化用のメンバ関数です。 A( int i_ ) : i(i_){} //上のメンバint i;を引数のi_で初期化します。 void IncrementI(){ ++i; } /* メンバ関数の()の後ろにconstを付けると,その関数中では特殊なことをしない限り メンバ変数は書き変えられる心配がありません。 */ int GetI() const { return i; } }; A a( 7 ); //コンストラクタに引数7を渡します。a.iは7になります。 a.IncrementI(); //a.iは8になります。 int k = a.GetI(); //kにa.iの現在の数値を代入します。 a.i = 0; /* これはエラー!privateなメンバに外から勝手にアクセスできません。コンパイルは失敗します。*/ こういう風にすることで、不必要なとこで間違って書き変えてしまう恐れを最小限にできるというわけです。

DEADSPACE566
質問者

お礼

回答ありがとうございます。 クラスのアクセス指定子で非公開、公開にできるんですよね。 C++なら構造体ではなくクラスを使うという方法の方がいいんでしょうね。

回答No.7

>ステップ数が多いと混乱の元なんでしょうかね。 そうですね。 よっぽど整理されたコードなら多少長くなってもその場その場での混乱が少ないですが 避けられる苦労ならわざわざ自ら選ぶことはありません。 >このxとyもテンプレート化したいといった事はできなんでしょうか? んん~~? どういう挙動がお望みなのでしょう? 下のコードでダメっていうのが逆にちょっと分かんない 間違ったコードでもいいので 理想をC++「っぽく」表せますか? ↓とりあえず現時点で、コレの真意、うまみ、骨の髄までしみ渡っていますか?  template < class T > struct RECTEX { T x; T y; void Function() const { std::cout << x << std::endl; std::cout << y << std::endl; } }; この場合の利点は Functionは「自分自身の構造体のメンバの名前」を使ってるだけなんで xやyって名前を間違う事は、打ち間違え以外にないだろうってとこにあります。 別の型を作りたかったら、簡単に作れますね RECTEX<double> a; RECTEX<int> b; RECTEX<short> c; RECTEX<long> d; メンバの名前を変えたければサクサクっと変えれますね template < class T > struct RECTEX { T 山田が光ると結果的に山田; T 伸びろ山田; void Function() const { std::cout << 山田が光ると結果的に山田 << std::endl; std::cout << 伸びろ山田 << std::endl; } }; これは普通のクラスや構造体とおんなじことです。 struct RECTEX { int a, b; void Function() const { std::cout << a << std::endl; //メンバを使うにはメンバ名が正しくないといけない。 std::cout << b << std::endl; } }; 逆にいえば、注意することはこれだけで済むという事です。

DEADSPACE566
質問者

お礼

詳しい解説ありがとうございます。 構造体の中の変数の型も自在に変えら得るようになれたらと思ったんです。 しかしそれでコードが複雑化するなら避けた方がいいと思いました。

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

「メンバへのポインタ」を使えばなんとかなるかと思ったけど, データメンバの場合には static じゃないとテンプレートの引数には渡せなさそう. とりあえず思い付くのは ・メンバへのポインタについてはテンプレートにするのをあきらめる ・必要なメンバをとりだす関数 (あるいはそのような機能を持つクラス) を作ってテンプレート引数に渡す くらい.

DEADSPACE566
質問者

お礼

回答ありがとうございます。 どうやらテンプレートのテンプレートとかは駄目みたいですね。

回答No.5

>ところで, stdio.h を #include しても cout は使えないような.... たぶん 省略されてるんじゃないでしょうかねw と、思って書いたんだけどもw 一応書くと #include <iostream> using namespace std; が、省略されてるはず、ってことでしょうね。 私的にはソースであってもグローバルに using namespace std; はさすがにちょっとお勧め致しかねますが。(小さいソースでは別にいいかもしれませんが) 関数内での using namespace std; cout, endl とかあるいは最初から std::cout, std::endl とかにする方が 副作用の恐れはだいぶ少ないでしょうね。

DEADSPACE566
質問者

お礼

回答ありがとうございます。 グローバルでの名前空間はしない方がいいとどこかのサイトで 観たことがあります。 ステップ数が多いと混乱の元なんでしょうかね。

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

うぅ~ん, 「構造体のメンバ名は統一しないとダメ」ってのは当たり前だよねぇ. だって, 「の要素x」 とか言い切っちゃってるんだもの. まあこの辺はより具体的に「こんなことがしたい」というのが出てくればそれに合わせるんだけど. ところで, stdio.h を #include しても cout は使えないような....

DEADSPACE566
質問者

お礼

回答ありがとうございます。 x,yと記述したらそれに合わせなければならないんですね。 このxとyもテンプレート化したいといった事はできなんでしょうか? 質問を重ねるようですみませんが。

DEADSPACE566
質問者

補足

間違いでした。 #include <iostream> using namespace std; ですね。

回答No.2

こんばんは。 この場合だとこれで良いです。 template <class T> T Function(T abc){ cout << abc.x << endl; cout << abc.y << endl; return abc; } templateは使用箇所が出てくるまでコードの生成を遅延するという性質があります。 そして「その時に初めて整合性がチェック」されますのでこれで問題ありません。 (ただし型ごとに似たものが内部で作られるので、型が多いと実行ファイルサイズは膨張する可能性があります。) ただ、abcのコピーコンストラクタが無駄に呼ばれることになりますし この場合abcをそのまま返す理由もないので template <class T> void Function(const T& abc){ cout << abc.x << endl; cout << abc.y << endl; } こちらの方がよろしいかと思います。 ただ、この場合ですと structのメンバの型が違うだけですので クラスtemplateを使うのがよろしいかと思います。 ついでに C++ではtypedef struct {} 何々; としなくても struct 何々{}; だけで 何々 hoge; と使う事ができます。 以上を踏まえて、構造体の内部に持たせてしまう方法 template < class T > struct RECTEX { T x; T y; void Check() const { cout << x << endl; cout << y << endl; } }; で十分です。 この場合は int main(void){ RECTEX<int> t = {80,90}; RECTEX<double> r = {1.0,2.0}; t.Check(); r.Check(); return 0; } このような形になります。

DEADSPACE566
質問者

補足

 ありがとうございます。 さっそく試してみました。 構造体のメンバ名は統一しないとダメみたいです。 template <calss T> T Function(T abc) { // 構造体の要素にも対応している。 統一する cout << abc.x //の要素x << endl; cout << abc.y //の要素y << endl; return T; }

回答No.1

return T; を return abc; に変えたらどうでしょうか。

DEADSPACE566
質問者

お礼

回答ありがとうございます。 確かにその通りですね。 この場合 return T; です。

DEADSPACE566
質問者

補足

return T ではなくreturn abcでした。 すいません

関連するQ&A

  • 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]と値は違い、別の特殊化テンプレートが使われています。 この部分が分かりません。 また、配列リテラル、文字列リテラルに対し部分特殊化テンプレートを宣言する方法などありましたら、ご教示お願いします。

  • C++のことで・・教えてください。

    下記のプログラムはコンパイルはできますが、実行するとエラーとなり強制終了させられてしまいます。いつたいなぜなのでしょうか? #include<iostream.h> struct point{ int x; int y; }; struct pairpoint{ point so; point si; }; pairpoint* h = (pairpoint*)malloc(1); int main(){ h[0].so.x = 1;h[0].so.y = 3; h[0].si.x = 5;h[0].si.y = 7; cout << h[0].so.x << "," << h[0].so.y << endl; cout << h[0].si.x << "," << h[0].si.y << endl; }

  • プログラムのどこがエラーか教えてください(C++)

    問題は、buildingという基本クラスを作って、建物の階数、部屋数、床の総面積を格納するクラスを作成し、次にhouseという派生クラスを作り、buildingを継承し、寝室数と、浴室数を格納するクラスで、officeという派生クラスを作り、これもbuildingを継承し、消火器台数と、電話台数を格納するクラスを作成し、それぞれの結果を出力せよ。っていう問題です。 下に僕が作成したプログラムを書きます。雑なので見にくいかもしれませんがお願いします。 #include <iostream> using namespace std; class building{ int Floor,Room,Area; public: building(int f,int r,int a){ Floor=f; Room=r; Area=a; } void get_FRA(int &f,int &r,int &a){ f=Floor; Room=r; Area=a; } }; class house:public building{ int Bedroom,Bathroom; public: house(int f,int r,int a,int Be,int Ba):building(f,r,a){ Bedroom=Be; Bathroom=Ba; } void Hshow(); }; class office:public building{ int FireExtinguisher,Telephone; public: office(int f,int r,int a,int fe,int t):building(f,r,a){ FireExtinguisher=fe; Telephone=t; } void Oshow(); }; void house::Hshow() { int x,y,z; get_FRA(x,y,z); cout << "階数:" << x << "階建て" << endl; cout << "部屋数:" << y << "室" << endl; cout << "床の総面積:" << z << "m2" << endl; cout << "寝室:" << Bedroom << "室" << endl; cout <<"浴室:" << Bathroom << "室" << endl; } void office::Oshow() { int x,y,z; get_FRA(x,y,z); cout << "階数:" << x << "階建て" << endl; cout << "部屋数:" << y << "室" << endl; cout << "床の総面積:" << z << "m2"<< endl; cout << "消火器の数:" << FireExtinguisher << "個" << endl; cout << "電話の数:" << Telephone << "個" << endl; } int main() { house H_ob(5,2,300,3,2); office O_ob(3,5,500,2,8); H_ob.Hshow(); O_ob.Oshow(); return 0; } あと、プログラムの処理結果を貼っておきます。 できれば、詳しく教えていただければ幸いです。

  • 構造体・ビットフィールドのvolatileに関して。

    以下のような構造体があったとする。 -(1) -----------------------------  typedef volatile struct ABC{    char x;    char y;  }stABC; -(2) -----------------------------   typedef struct ABC{    volatile char x;    volatile char y;  }stABC; --------------------------------- ■質問  -------------   stABC abc;   abc.x // ←volatileは有効?   abc.y // ←volatileは有効?  ------------- ・このとき、メンバx,y は(1)/(2)の構造体型宣言どちらでも  同じ意味合いになっているのでしょうか。  ※特に(1)の場合、volatileは有効になっているのでしょうか。 ・ビットフィールド時も同じと考えて問題ないでしょうか。   (1)typedef volatile struct ABC{      char x: 1 ;      char x: 7 ;    }stABC;   (2)typedef struct ABC{      volatile char x: 1 ;      volatile char x: 7 ;    }stABC;

  • C++ template コンパイルできないパターン

    度々お世話になります。 以下のソースがコンパイルできません。 ご存知の方がいらっしゃれば教えて下さい。 #include <iostream> #include <exception> template<typename T, T C> inline T check(T x) { if (x == C) { throw(std::exception()); } return x; } int main() { using namespace std; int x; try { char * p = "abc"; check<char*, 0>(p); // コレがコンパイルできない int i = 2; check<int, 4>(i); // コレはコンパイルできる } catch (...) { cerr << "err" << endl; } return 0; } 手元の環境だと % g++ foo.cc foo.cc: In function 'int main()': foo.cc:18: error: no matching function for call to 'check(char*&)' となります。 関係するのか分からないのですが char* をテンプレートの引数にしているのに コンパイラのエラーメッセージは char*& となっているのがよく分かりません。 目的としては、エラーチェックをして エラーをであれば、例外を投げるというものです。

  • c++テンプレート関数名

    #include <iostream> #include <string> using namespace std; template <class T> T maxdt std(T a,T b){ if (a>b) return a;else return b; } int main (){ int id1,id2,n1 = 1000,n2 = 2000; double ddt; string sdt,s1 = "abcd",s2 = "jklm"; id1 = maxdt(n1,n2); id2 = maxdt(3000,4000); ddt =maxdt (55.55,66.66); sdt = maxdt(s1,s2); cout << "id1" << id1 <<endl; cout << "id2" << id2 <<endl; cout << "ddt" << ddt <<endl; cout << "sdt" << sdt <<endl; return 0; } というプログラムが例題であり、僕は横着をしてmaxdtをmaxという名前としてプログラムを作ったところ、 オーバーロード関数の呼び出しがあいまいです。 というエラーが表示されコンパイルできませんでした。 環境はubuntu 12.04,gcc 4.6.3 g++でコンパイルしました。 ググってもテンプレート関数の名前の付け方に規則は存在していないようなのですが、 これは直前に作ったプログラムの影響でしょうか?(maxという関数を作っていたので) それとも別の規則が存在したりするのでしょうか?

  • 構造体の中の構造体

    typedef struct number{ int x; struct number *next; }Num; 初心者な質問で申し訳ないんですが、構造体の中に構造体があるのはどう解釈していいんでしょうか? typedef struct number{ int x; int y; }Num; の場合はNum a,b;がint a.x,a.y,b.x,b.yとなるのは分かるんですが・・・

  • テンプレートについて

    最近テンプレートを勉強し始めました。 試しに次のような関数を書いたのですがコンパイルエラーが出ます。 template<class T> void pirntAll(T t) { T::iterator p; for(p=t.begin();p!=t.end();p++) cout<<*p<<" "; cout<<endl; } エラーメッセージを見るとT::iterator p;のところがダメらしく pが定義されていないと叱られます。 結局本などを参考にして次のように書き換えました。 template<class InputIterator> void printAll(InputIterator first,InputIterator last) { while(first!=last) { cout<<*first<<" "; first++; } cout<<endl; } しかしprintAll()を使うとき1つめの定義ならprintAll(x);と書けますが 2つめの定義だとprintAll(x.begin(),x.end());と書かなくてはならないので 面倒です。そこで2つめの定義と次の関数を組み合わせることで、コンパイルも通り、 使うときもprintAll(x);と書けるようにしました。 template<class T> void printAll(T t) { printAll(t.begin(),t.end()); } 一応問題は解決したのですが何かひどく冗長なことをやらされているようで 気分が悪いです。なんとか1つめのような書き方ができないものでしょうか。 または1つめの書き方が出来ない(T::iteratorが使えない)正当な理由が あるなら教えてください。

  • C++ ソートのやり方

    僕が作ったプログラムで、これはバブルソートなのかわからないので教えてください。 また、ほかのソートの仕方も教えてください。 よろしくお願いします。 汎用関数を使っているのでわかりにくいかもしれないですがお願いします。 #include <iostream> using namespace std; template <class X>void Sort(X *data, int size) { X temp; for (int i = 0; i < size; i++){ for (int j = i + 1; j < size; j++){ if (data[i]>data[j]){ temp = data[i]; data[i] = data[j]; data[j] = temp; } } } } int main() { int i[10]{1, 4, 3, 5, 2, 10, 2, 7, 6, 8}; char c[10]{'c', 'b', 'z', 'a', 'x', 'y', 'j', 'n', 'm', 'r'}; Sort(c, 10); Sort(i, 10); for (int j = 0; j < 10; j++){ cout << i[j] << ' '; } cout << endl; for (int j = 0; j < 10; j++){ cout << c[j] << ' '; } cout << endl; getchar(); return 0; }

  • Yesならこっちへ、NOならあっちへ(c++)

    取り組んでる課題で、Yとタイプしたら1のステップにいけて、Nとタイプしたら2のステップに行くというところでわからなくなってしまいました。Userにそういう選択させるのには何をどう書いたらいいのですか? --------------------------------------------------------------------- 問題:1から100までの整数をUserに選ばせ、Userに、「選んだ数字はXX以上ですか?」と質問を繰り返し、最後にUserの選んだ数字を当てるという課題です。(Userはそれに対してYes/Noでしか答えられません。) --------------------------------------------------------------------- int max=100; int min=0; int mid, x; int systemtype=y, sytemtype=n; main(){ while(1){ cout<<"1から100までで好きな数字を選んでね。"; cin>> x; if (n<=0 || n>=100){ break; } if (mid == (max + min)/2){ cout<<"選んだ数字は :" << mid << " より大きい? "<< endl; cout<<" y は YES, n は NO :" << endl; } else if(min == max){ cout<<"その数字は" << x <<endl; break; } else if(min == mid){ mid += ( max - mid)/2; cout<<"あなたの選んだ数は :" << mid <<" より大きい? "<<endl; } else if(max == mid){ mid -= (mid - min)/2; cout<<"あなたの選んだ数は:" << mid << " より大きい? "<< endl; } else cout<<"その数字は :"<< n <<endl; } return 0; }

専門家に質問してみよう