テンプレート関数とwstring,stringの処理の違いについて

このQ&Aのポイント
  • テンプレート関数を使用して、wstringとstringに対して似たような処理を行いたい場合、Tによって値の変更が可能かどうかを調べたい。
  • また、似たような処理を行いたいが、変数の型を制限する方法はあるかを知りたい。
  • これらの質問についてご回答します。
回答を見る
  • ベストアンサー

テンプレート関数に関して質問です。

テンプレート関数に関して質問です。 wstringとstringに対して、似たような処理を行いたいので、template関数を使ってうまく定義できないかと思ったのですが、例えば、 #include<string> template<class T> void hoge(T &s){ s="a"; } void main(void){ std::string str; hoge(str); } の場合、class TがwstringだとL"a"としないといけないのですが、Tによってこれを変えることは可能でしょうか? また、できれば、hogeに想定外の変数を入れられたくないので、hogeをtypedefのようなのりで、 void foo(std::string &s); void foo(std::wstring &s); と定義しておきたいのですが何か良いアイデアはないでしょうか? 当然、hogeはstaticグローバル変数のように外部からはアクセスできないようにしておきたいのです。 よろしくお願いします。

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

  • ベストアンサー
  • titokani
  • ベストアンサー率19% (341/1726)
回答No.7

マクロで書くなら、 #define make_func(T) \ void hoge(T &s)\ {\ s=TX("a");\ } #define TX(s) s make_func(std::string); #undef TX #define TX(s) L##s make_func(std::wstring); #undef TX これがせいぜいでしょうか。

glarelance
質問者

お礼

なるほど、これはいい感じですね。 #define make_func(T,TX) \ void hoge(T *s)\ {\ s[0]=TX##'a';\ } make_func(char,); make_func(wchar_t,L); という感じで利用してみることにします。 どうもありがとうございました。

その他の回答 (6)

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.6

無名名前空間というのは、その名の通り「名前のないnamespace」です。 この空間内に記述されたものは外部ファイルから事実上アクセス不能になります。名前指定できないので。 マクロで作るというのは、テンプレートを使う代わりに全部マクロで書くということです。 #define make_func(T) \ void hoge(T &s) \ { \ s = "a" \ } make_func(wstring); make_func(string); こうすると、そもそもテンプレートでないため他の型は受け付けようがない、という訳です。 尤も、見ての通り特殊化は出来てませんが。

glarelance
質問者

お礼

色々テストしていて返信が遅れすみません。 これは、私のやりたいことにものすごく近いので、ものすごく惜しいですね・・・ #define make_func(T) \ #if T==char #define _TX_ #else #difine _TX_ L #endif void hoge(T &s) \ { \ s = _TX_"a" \ } みたいなこと出来ないかなーと試していたのですが、無理なようでした。 こんな感じのことは、マクロでは無理なんでしょうか?

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.5

こんな感じでできますが、これだとテンプレートにする意味がないですね。 template<class T> void hoge(T &s); template<> void hoge(std::string &s) { s="a"; } template<> void hoge(std::wstring &s) { s=L"a"; } たとえば、 void init(std::string &s) { s="a"; } void init(std::wstring &s) { s=L"a"; } template<class T> void hoge(T &s) { init(s); } こんなんでどうでしょう?

glarelance
質問者

お礼

うーん。それだと、関数の中で使われている全ての文字列に対して、関数を作らないといけなくなってしまいますね・・・

回答No.4

 こんばんは。  template<>については、テンプレートの特殊化というものです。  http://www.geocities.jp/ky_webid/cpp/language/037.html  Tにstd::stringが入れられた場合と、std::wstringが入れられた場合で、別の場所をコンパイルしているという事です。それ以外の方が入ればコンパイルエラーです。  ::については、::の前に何も書かれていなければ、そういう事になります。  http://www.geocities.jp/ky_webid/cpp/language/018.html  

glarelance
質問者

お礼

なるほど。よく分かりました。

  • colder
  • ベストアンサー率43% (30/69)
回答No.3

ここが参考になるかと http://ml.tietew.jp/cppll/cppll/article/13146

glarelance
質問者

お礼

解答ありがとうございます。 これは、なかなか良さそうなソースですね。 ただ、これだと、マルチバイト文字とワイド文字を両方保持することになるので、できれば、マクロのようにコンパイル時にwchar_t用の関数とchar用の関数を生成できるのが望ましいのですが・・・ void func(){   _T_CHAR *c=_TTX_"abc"; } のようなのを、コンパイル時に別のところへコピーして貼り付けて、 #define _T_CHAR char #define _TTX_ #define _T_CHAR wchar_t #define _TTX_ L というかんじで使い分けられたら一番良いのですが、この辺りがC++の限界ということでしょうか?

回答No.2

 こんばんは。  L"a"の代わりに以下のAPIで強引に変換してみては。  http://msdn.microsoft.com/ja-jp/library/cc448053.aspx  以下参考程度に。 #include<windows.h> #include<string> template<class T> struct Convert; template<> struct Convert<std::string> { //変換の必要がないのでそのまま通す static const std::string get(const std::string& mbcs) { return mbcs; } }; template<> struct Convert<std::wstring> { //LPSTRからLPWSTRへの変換 static const std::wstring get(const std::string& mbcs) { const int len = ::MultiByteToWideChar(CP_ACP, 0, mbcs.c_str(), -1, NULL, 0); LPWSTR p = (LPWSTR)::malloc(len * 2); ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, mbcs.c_str(), -1, p, len); const std::wstring w(p); ::free(p); return w; } }; template<class T> void hoge(T &s) { s = Convert<T>::get("a"); } void foo(std::string &s) { ::hoge(s); } void foo(std::wstring &s) { ::hoge(s); } int main() { std::string str; ::foo(str); std::wstring wstr; ::foo(wstr); return 0; }

glarelance
質問者

お礼

解答ありがとうございます。 しょうもないことですみませんが、template<>のような無記名のtemplateは初めてみたのですが、どういう意味になるのでしょうか? また、::はどういう意味で使われているのでしょうか? グローバル空間に定義されてる関数を指しているという感じの意味ですか?

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.1

テンプレートでそれを実現するのは無理かと思います。 非共通部分は別々に実装し、共通部分のみテンプレートで合わせこむのがいいんじゃないでしょうか? 後者の方は、試した事はないですがテンプレートを無名名前空間内に放り込む事ができれば、外部からのアクセス遮断は行けるような気がします。 テンプレートではなくマクロで作ってしまえば固定的に展開できるので、想定外の型を入れることもできなくなりますが。

glarelance
質問者

お礼

解答ありがとうございます。 >テンプレートを無名名前空間内に放り込む事ができれば 馬鹿なことを聞いてるかもしれませんが、無名名前空間に放り込むとはどのようなことを仰っているのでしょうか? >マクロで作ってしまえば固定的に展開できるので、想定外の型を入れることもできなくなりますが。 マクロで作るというのは、どのようにすることを仰ってるのでしょうか?

関連するQ&A

  • templateクラスについて

    先ほど以下のようなプログラムを書いたのですがコンパイルを通すことができません。 //適当なポインタを保持するだけのクラス template <class _type> class hoge { private:   //適当に変数を保持   _type val; public:   //コンストラクタで適当に値をセット   hoge() : val( 0 ){}   //このクラスから唯一ポインタを引っ張ってくる方法   friend _type getVal( const hoge& foo )   {     // そのまま返す     return foo.val;   } }; void func( const hoge<int>& foo ) {   //値を引き出す   getVal( foo ); } void main() {   //実体化   hoge<int> foo;   //値を引き出す   getVal( foo );   //関数の先で値を引き出す   func( foo ); } 上記のようなプログラムを書いたのですが、main関数内でgetValを呼び出す場合はとくに問題ないのですがfunc関数を呼び出してfunc関数内でgetValを呼び出すと error C3861: 'getVal': 識別子が見つかりませんでした error C2365: 'getVal' : 再定義; 以前の定義は '以前は不明な識別子' でした。 コンパイルされたクラスの テンプレート のインスタンス化 'hoge<_type>' の参照を確認してください というエラーが出てしまいます。 func関数の引数を( const hoge<int>& foo )からvoid func( hoge<int> foo )のように参照渡しから実体渡しに変更するとコンパイルが通り、実行もできるのですが、なぜこれでコンパイルが通るのか理由がいまいちよくわかりません。 またやはり、コンストラクタ、デストラクタの問題などから実体渡しより、参照渡しを使いたいのですがどのようにプログラムを書けば今回の問題を解決できますでしょうか。 よろしくおねがいします。 /* VisualStudio2005 AcademicEdition MicroSoft WindowsXP Professional 32bit */

  • STL に適合するようにクラスを書くとき皆さんはどうしているのでしょう

    大した質問ではないのですが、例えば適当に vector っぽいクラスを template<typename T> class HeppokoVector { private:  いろいろ省略 public:  void push_back(T const& val); }; のように設計したとしましょう。ヘッポコとは言え vector なので STL の std::copy を使って、HeppokoVector<char> hoge; に既存の vector<char> foo; の内容を std::copy(foo.begin(), foo.end(), std::back_inserter(hoge)); な感じでコピーしたいと思いましたが、コンパイルすると 「reference : は HeppokoVector<T> のメンバーではありません」 などと怒られます。back_insert_iterator の定義を見れば、reference や const_reference の定義が必要なので、 template<typename T> class HeppokoVector { private:  いろいろ省略 public:  typedef T& reference;  typedef T const& const_reference;  void push_back(T const& val); }; な風に修正するわけですが、皆さんは自前でコンテナを作る時などに、あらかじめ「コレコレの typedef は必要だな」とか知った上で作成しとるのでしょうか? きっと私が適当なだけなのでしょうが、back_insert_iterator のドキュメントを見ても、push_back が必要であることしかわかりませんでした。

  • 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という関数を作っていたので) それとも別の規則が存在したりするのでしょうか?

  • テンプレートクラスのフレンド関数の宣言

    テンプレートクラスに対して、operator << を定義しようとしてハマってしまったので。 ---- 最終的にできたコードはこんな感じ ---- template<size_t M> class MyContainer; template<size_t N> std::ostream& operator<<( std::ostream& os, MyContainer<N> const& cont ); template<size_t M> class MyContainer {  friend   ostream& operator<< <M>( ostream& , MyContainer<M> const& ); <= ここで「operator<<」としてハマった  public:   MyContainer() { }  private:   void Print_( std::ostream& os ) const {    copy( content_, content_ + M, ostream_iterator<int>( os, "\n" ) );   }  private:   int content_[M]; }; template<size_t N> ostream& operator <<( ostream& os, MyContainer<N> const& cont ) {  cont.Print_( os );  return os; } ------------------------------ テンプレートクラスのfriend関数を宣言する場合に、関数に明示的にテンプレート引数を与えないとテンプレート関数の特殊化だけが friend となるようです。 そういうもんだと言ってしまえばそれまでですが、何故こんな変態的な仕様になってるんでしょう? 特殊化された関数だけをテンプレートクラスのfriend に指定したいような状況が想像できません。

  • 関数に0を代入??

    研究に使うため,C言語のプログラムを読んでいます. 下記のようなプログラムがあるのですが,関数のプロトタイプに0を代入しています,この部分が意味が分からないので,どなたか分かる人がいたら教えてください. class CLASS{ public: virtual void setVar (std::string& rcValue) = 0; }; void ConfigLineChar::setVar( std::string& pvValue ) { *m_pcPar = (Char)atoi( pvValue.c_str() ); } 不必要な部分は削除して書いています.

  • テンプレートについて

    最近テンプレートを勉強し始めました。 試しに次のような関数を書いたのですがコンパイルエラーが出ます。 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が使えない)正当な理由が あるなら教えてください。

  • コンパイルエラー

    //test.cpp #include<iostream> #include<string> class test { string hoge; public: test(string str); void method() const; } 14行目  test::test(string str) : test(hoge){} void test::method() const { std::cout << "test" << std::endl; } int main() { string s; std::cout << "名前を入力してください。" << std::endl; std::cin >> s; test aaa(s); aaa.method(); } コンパイルしようとすると下記エラーになります。 エラー E2303 test.cpp 7: 型名が必要 エラー E2139 test.cpp 7: 宣言に ; がない エラー E2293 test.cpp 10: ) が必要 エラー E2147 test.cpp 14: 引数宣言は 'string' で始められない エラー E2111 test.cpp 14: ここでは型 'test' は定義できない エラー E2136 test.cpp 14: コンストラクタには戻り型は指定できない エラー E2316 test.cpp 14: 'test::test(int)' は 'test' のメンバーではない エラー E2451 test.cpp 22: 未定義のシンボル string(関数 main() ) エラー E2379 test.cpp 22: ステートメントにセミコロン(;)がない(関数 main() ) エラー E2451 test.cpp 24: 未定義のシンボル s(関数 main() ) *** 10 errors in Compile *** ■1.型名が必要 →string hoge;と宣言してる箇所でエラー発生。 …stringと宣言してるのになぜでしょうか… ■2.宣言に";"がない →string hoge;と宣言してる箇所でエラー発生。 …";"つけてるのですが… ■3.")"が必要 →test(string str);の行でエラー発生。 …")"はどこで必要なのでしょうか ■4.引数宣言はstringでははじめられない →test::test(string str) : test(hoge){}の行でエラー発生。 …C++って引数をstringで受け取れない…?どういうことでしょうか などなど疑問がいっぱいあるのですが、どこが間違っているのかよくわかりません…ご指導よろしくお願いします。

  • テンプレート引数の型推測

    コンパイラはVC++2008です。 いろいろあって、あるクラスにおいて関数ポインタと関数オブジェクト双方を 同じように利用できないかと考えて、次のように試みました。 class Base { public:     virtual void func() =0; }; template<class Func> class CFunc :public Base { private:     Func m_func; public:     CThreadFunc(Func func):m_func(func){}     void func(){m_func();} }; class Hoge { private:     Base* base; public:     template<class Func>     Hoge(Func func)       :base(new CFunc<Func>(func))     {}     ~Hoge()     {       delete base;     }     void DoSomething()     {       base->func();     } }; クラスをテンプレートにするといちいち指定しなければならないので、 まず基底クラスに適当な仮想関数を設け、それを継承したクラスをテンプレートにしました。 そしてコンストラクタの引数で何かしらを受け取って、オーバーライドした関数の中で 関数ポインタか関数オブジェクトだと仮定して呼び出しています。 さらに基底クラスのポインタを目的のクラスが保持してやり、 こちらはコンストラクタをテンプレートにすることで引数から型を推測してもらうことで 先ほどのテンプレートクラスのインスタンスを作成しています。 そしてポインタを介してfunc()を使ったり…、などすれば、 とりあえず引数なしの関数と関数オブジェクトを同等に扱えないかなと思ったからです。 で、このようなクラスを作成してコンパイルすると、 void func(); //何かしら処理する関数 class Function { public:   void operator ()();  //何かしら処理する関数オブジェクト }; があったとして、 int main() {   Function function;   Hoge hoge(function); //いったん作ってから渡す   Hoge hoge2(func); //関数を渡す     hoge.DoSomething();   hoge2.DoSomething();    } は動きました。しかし、 int main() {   Hoge hoge(Function()); //引数を初期化する } とすると次のようなエラーが出ます。 warning C4930: 'Hoge hoge(Function(__cdecl *)(void))': プロトタイプされている関数が呼び出されませんでした (変数の定義が意図されていますか?) また、 int main() {   Hoge hoge(Function()); //引数を初期化する   hoge.DoSomething();  //クラスにアクセス } とすると次のようなほかのエラーが出ます。 error C2228: '.DoSomething' の左側はクラス、構造体、共用体でなければなりません。 しかし、例えば関数オブジェクトのコンストラクタに引数が設定されていたとして、 class Function { public:   Function(int dummy);  //何か値を受け取る   void operator ()();  //何かしら処理する関数オブジェクト }; となっていた時、 int main() {   Hoge hoge(Function(1)); //引数を初期化する   hoge.DoSomething();  //クラスにアクセス } の呼び出しは正常にコンパイルされ、想定通りの動きをします。 全く使わなくても、一つ以上の適当な引数を何でもいいからコンストラクタが持てば、 普通にコンパイルされるみたいです。ただ、デフォルト引数を与えてHoge hoge(Function())と 同じ形ですと引数があってもできないみたいです。 まったく通らないなら最初からあきらめるですが、中途半端にちゃんと動くために エラーの原因を知りたいと思っています。 テンプレートの場合には、引数に渡すタイミングで初期化はしてはいけないのでしょうか?

  • 仮想関数について困っています

    仮想関数について困っています C++を現在勉強中でその中で困ったことができました。 仮想関数についてまだ分かっていないことが多いのですが、一応以下のように使うものだと学びました。 class test{  public:   virtual void run(){    std::cout<<"testクラス"<<std::endl;   } }; class test_sub:public test{  public:   void run(){    std::cout<<"test_subクラス"<<std::endl;   } }; int main(){  test *t_s;  t_s=new test_sub;  t_s->run(); } ※includeは省略させていただきます こうすれば「test_subクラス」と出力されるはずです。 そこで本題なのですが自作のarrayクラスのようなものはテンプレートクラスになっているのですが array<test*> data; data[0].run(); のように使うと「testクラス」と表示されてしまいます。 これを解決する方法は何かないでしょうか? ご存じの方がいましたら教えていただけると助かります。

  • メンバ関数ポインタを値とするコンテナの定義方法

    こんにちは、boundaryといいます。 あるクラスのメンバ関数のポインタをmapで管理したいので すが、定義でエラーになってしまいます。 class CFunc { private: bool FuncA(); bool FuncB(); }; このようなクラスに対して、 template <class T> class FuncMap { private: typedef bool (T::*Func)(); typedef map<int, T::Func> _FuncMap; _FuncMap FMap; public: void set(int, T::Func); T::Func search(int); }; を用意しました。 template <class T> void FuncMap<T>::set(int i, T::Func pFunc) { FMap.insert(pair<int , T::Func>(i ,pFunc)); } template <class T> T::Func FuncMap<T>::search(int i) { map<T::Func, int>::iterator FuncMapIte; FuncMapIte = FMap.find(i); return T::Func; } と書いたのですが、 error C2244: 'FuncMap<T>::set' : 関数のオーバーロードが解決できません。 error C2954: テンプレートの定義はネストできません。 error C2244: 'FuncMap<T>::search' : 関数のオーバーロードが解決できません。 との事です。 色々試行錯誤したのですが、うまくいきません。 定義のどこがいけないのでしょうか? よろしくお願いします。 VC6.0SP5 WINDOWS2000です。

専門家に質問してみよう