テンプレート指定クラスのコンストラクタを明示呼出

このQ&Aのポイント
  • テンプレート指定クラスのコンストラクタを明示的に呼び出す方法を教えてください
  • 特定のクラスに対するコンストラクタの明示的な呼び出しはできましたが、テンプレートで一般的に対応する方法はわかりません
  • deleteの実装は意図通りできました
回答を見る
  • ベストアンサー

テンプレート指定クラスのコンストラクタを明示呼出

自前でnewのような関数を作っています。 mallocで確保したメモリ領域のそれぞれの項目に対してコンストラクタを呼び出したいのですが、 テンプレートで指定されたクラスのコンストラクタを明示的に呼ぶにはどうしたらよいですか? テンプレート関数でなく、特定のクラスに対するものならできたのですが、 テンプレートで一般的に対応しようと思った所で躓きました。 また、deleteのほうは意図通り実装できました。 struct HOGE{ HOGE(){} }; //特定のクラスに対するものは正常 HOGE *mynew(size_t len){ HOGE *v = (HOGE*)malloc(len * sizeof(HOGE)); for(size_t t = 0; t < len; ++t){ //v[t].HOGE(); //error C2274: '関数形式のキャスト' : '.' 演算子の右側で使用できません。 v[t].HOGE::HOGE(); //OK } return v; } template<typename T> T *mynew(size_t len){ T *v = (T*)malloc(len * sizeof(T)); for(size_t t = 0; t < len; ++t){ v[t].T::T(); //error C2039: 'T' : 'HOGE' のメンバーではありません。 //代わりに以下のように書いてみたが若干危険で効率も悪そう T tmp = T(); memcpy(v + t, &tmp, sizeof(T)); memset(tmp, 0, sizeof(T)); } return v; } template<typename T> void mydelete(T *v, size_t len){ for(size_t t = 0; t < len; ++t){ v[t].~T(); //OK } free(v); } HOGE *h = mynew<HOGE>(3); //NG HOGE *j = mynew(3); //テンプレート関数でない方は呼べる mydelete(j, 3); //OK

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

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

placement newを十分理解していないようですね。 C++で未初期化の領域に対して、明示的にコンストラクタを呼び出す方法は、placement new以外にはありません。 やりたいことは以下のようなものではないでしょうか template<typename T> T *mynew(size_t len){ T *v = (T*)malloc(len * sizeof(T)); for(size_t t = 0; t < len; ++t){ new(&v[t]) T(); } return v; }

haporu
質問者

お礼

placement newの記事を探しているうちに、当初の「渡されたポインタをそのまま返すnew」を知らずに、後から知られた使い方「任意の引数を渡せるnew」を先に知ってしまい、それを自分で定義しようとしてごちゃごちゃになっていたようです。 newをインクルードして教えていただいたコードが実行できました。 ありがとうございました。

haporu
質問者

補足

返事がおそくなってすいません。 なるほど、外部で確保したポインタを渡すことで、newをコンストラクタを呼び出すためだけに使うということですね。 newの構文が複雑なので、単にコンストラクタを呼ぶというテンプレート関数にして、mallocの部分と分離してしまった方がわかりやすそうです。 この方法をとってみようと思います。

その他の回答 (3)

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

> テンプレート関数でなく、特定のクラスに対するものならできたのですが、 本当ですか? > v[t].HOGE::HOGE();//OK これは何をしているのか、ちゃんと理解していますか。

haporu
質問者

補足

ありがとうございます。 v[t]にたいしてコンストラクタを呼んでいるつもりなのですが、間違っているでしょうか。

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

「これでおおよそ目的が果たせそうなのですが、operator new[]とoperator delete[]は定義できたのですが、operator newとoperator deleteはすでに定義されてるとでて、オーバーライド(?)できませんでした。」 と書かれていますが, operator new や operator delete が突然出てきているのはなぜでしょうか? 元の質問を読む限り, 「グローバルのnewとdeleteを置き換える」必要などないはずですが. いずれにしても, エラーメッセージは正確に書いてほしいし, 「どう書いたらどこでどのようなエラーが出たのか」を秘密にするようならまともな答えが返ってこない可能性は理解してほしい.

haporu
質問者

補足

placement newを調べるとoperator newにたどり着きました。 グローバルのnewとdeleteを置き換えたときに出たエラーはヘッダにかいて__inlineを付け忘れていたため、定義の重複でした。すいません。 void *operator new(size_t sz){ return malloc(sz + 1); } error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) は既に inlineCheck.obj で定義されています。 グローバルなnewを置き換えたい理由は以下です。 1 要求された個数よりも1要素分余分なバッファを確保する 2 要求された個数分の要素に対して確保時にコンストラクタを、破棄時にデストラクタを呼ぶ。余分に確保された1要素分にはコンストラクタもデストラクタも呼ばず、0で埋める 3 intやコンストラクタを持たないオブジェクトは、未定義値ではなく0で埋める グローバルな一般的なnewを置き換えるのは、ライブラリを使うときに混乱するので、いろいろ調べた結果、余分な引数を持ったnewがあることを知ったので、それをつかって目的が果たせそうです。 (通常のnewではvoidと要求されたサイズだけが渡され、1要素分のサイズがわからなかったが、余分な引数を持つnewでそのサイズが渡せた) //配列の最後に要素0を追加して返すnull terminated new void *operator new[](size_t tsz, size_t tsz2){ tsz += tsz2; unsigned char *v = (unsigned char*)malloc(tsz); for(size_t t = 0; t < tsz; ++t){ v[t] = 0; } return v; } void operator delete[](void *v, size_t tsz2){ free(v); } template<typename T> T *ntnew(size_t tlen){ return new(sizeof(T)) T[tlen]; } template<typename T> void ntdel(T *v){ delete[] v; }

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

1st choice は placement new か. あるいは適当に typename を追加する?

haporu
質問者

補足

ありがとうございます。 placement newについて調べた所、自前定義のnewが呼び出された後、勝手にコンストラクタを呼んでくれるようです。 また、自前のdeleteが呼び出される前に、勝手にデストラクタを呼んでくれるようです。 これでおおよそ目的が果たせそうなのですが、operator new[]とoperator delete[]は定義できたのですが、operator newとoperator deleteはすでに定義されてるとでて、オーバーライド(?)できませんでした。 グローバルのnewとdeleteを置き換える方法はありませんか?

関連するQ&A

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

    現在、ヘッダファイル内で下記のようなクラスを宣言・定義しています。 // 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でコンパイルできればよいです。 よろしくお願いいたします。

  • 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)のようなこともできそうな感じがしたのですが、他に書き方があるのでしょうか。 どなたかご存知の方がいらっしゃいましたらご教示お願いします。

  • 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 の中に書くのも変な気がしました。

  • 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 */

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

    仮想関数をテンプレートにできないのは仕様と理解しているのですが、多少複雑になっても、それぞれの型に対するメンバ関数をいちいち記述していくことを回避するようなテクニックはありませんか? 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;} //... };

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

    以下のような文字列比較関数を作ろうと思っています。 ○配列変数であるときは配列版を、そうでないときはポインタ版を呼び出す  配列のサイズがわかるときは、仮に\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です。

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

    コンパイラは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())と 同じ形ですと引数があってもできないみたいです。 まったく通らないなら最初からあきらめるですが、中途半端にちゃんと動くために エラーの原因を知りたいと思っています。 テンプレートの場合には、引数に渡すタイミングで初期化はしてはいけないのでしょうか?

  • templateの使い方を教えて下さい。

    質問タイトルの通りです。 今、 unsigned char* AllocByteArray1d(unsigned long int n){ unsigned char *box; box = (uchar *)calloc(n, sizeof(uchar)); if(box == NULL){ puts("can't allocate memory..."); exit(1); } return box; } という関数があって、これはunsigned charの配列をとってくれる関数になってます。これをtemplateを使って、intの配列もとれるようにしたいんです。 恥ずかしながら試しに、 template <typename T> T* AllocByteArray1d(unsigned long int n){ T *box; box = (T *)calloc(n, sizeof(T)); if(box == NULL){ puts("can't allocate memory..."); exit(1); } return box; } とやってみましたがダメでした。事前にTのデータ型がわからないからだろうか、と思って template unsigned char* AllocByteArray1d(unsigned long int); を入れて実体化させてみましたが、これでもダメでした。 どこを修正すれば使えるようになるのか、C++に詳しい方に教えて頂けると幸いです。

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

    テンプレートクラスに対して、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 に指定したいような状況が想像できません。

  • ワイド文字列とコンストラクタ

    ワイド文字列(UNICODE)をクラス化しようとしているのですが、 コンストラクタにconst wchar_t*型の引数を持たせて呼び出すと、 コンストラクタに渡された文字列が消えてしまい、wcslen()でサイズを求めると何故かサイズも0になっています。 class WideString { public: WideString(const wchar_t*); … }; WideString::WideString(const wchar_t* ws) { ::OutputDebugString(ws); // ←文字列が消えてる?表示されない int len = wcslen(ws); // ←これが0 } int main(void) { WideString wStr(L"文字列"); … } これは一体どういうことなのでしょうか。どなたか教えてください。

専門家に質問してみよう