C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: クラステンプレートのメンバー関数ポインタ用部分特殊化


(過去ログ 119 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■69646 / inTopicNo.1)  クラステンプレートのメンバー関数ポインタ用部分特殊化
  
□投稿者/ キム (1回)-(2014/01/16(Thu) 22:39:19)

分類:[C/C++] 

2014/01/17(Fri) 00:22:05 編集(投稿者)

クラステンプレートを部分特殊化して関数ポインタ用の特別バージョンを作成しています。
そして、メンバー関数(インスタンス関数)ポインタ用の特別バージョン記述方法について質問があります。

// 標準バージョン(実装はしない)
template <typename>
class Test;

// 普通の関数ポインタ用特別バージョン
template <typename R, typename T1>
class Test<R (*)(T1)>
{
...
}

これを使うには、

Test<void (*)(int)> a;

のようにします。
で、普通の関数はアドレスなので、部分特殊化を

template <typename R, typename T1>
class Test<R (T1)>
{
...
}

のように書けば、

Test<void (int)> b;

のように使うことも出来ます。
次に、メンバー関数ポインタ用特別バージョンですが、以下のように書きました。

// メンバー関数ポインタ用特別バージョン
template <typename R, typename C, typename T1>
class Test<R C::(*)(T1)>
{
...
}

これを使うには、

Test<void (MyClass::*)(int)> c;

のようにします。
ところが、使う側から『普通の関数版と同じように書きたい(*を省略したい)』との要望があり、困っています。
普通の関数ポインタとは違うから出来ないと説明しましたが、知識不足で根拠を明確に出来ませんでした。
出来ない明確な根拠をご教示いただけると助かります。
または、もっと良い記述方法があればご教示いただけると助かります。

コンパイラはVC++2008です。
スマートデバイス用のビルドをするので2008より新しいバージョンは使えません。
boost等のライブラリは使用禁止、std::tr1名前空間も使用禁止という状況です。
(Visual C++ 2008 Feature Pack も未インストールです)

よろしくお願いします。
引用返信 編集キー/
■69660 / inTopicNo.2)  Re[1]: クラステンプレートのメンバー関数ポインタ用部分特殊化
□投稿者/ επιστημη (96回)-(2014/01/17(Fri) 21:43:30)
> または、もっと良い記述方法があればご教示いただけると助かります。
>
> コンパイラはVC++2008です。
> スマートデバイス用のビルドをするので2008より新しいバージョンは使えません。
> boost等のライブラリは使用禁止、std::tr1名前空間も使用禁止という状況です。

うーん... 縛りがキツすぎるかな。
function<R(T)> と lambda 使えばあっさりできちゃうんですけどねー

引用返信 編集キー/
■69661 / inTopicNo.3)  Re[2]: クラステンプレートのメンバー関数ポインタ用部分特殊化
□投稿者/ 774RR (130回)-(2014/01/17(Fri) 23:18:23)
結局のところ [普通の関数へのポインタ] と [メンバ関数へのポインタ] の違いを、
うるさがた同僚に納得できていただければそれでよい・・・ということならば。

普通の関数へのポインタと、メンバ関数へのポインタではサイズが違う
http://www7b.biglobe.ne.jp/~robe/cpphtml/html03/cpp03058.html

メンバ関数へのポインタを整数値から得ることはできない
http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+201302/13020010.txt

使うときにも (使う先で必要となる) this が必要とか、で納得してもらえないかなー。

単に記法が納得できない、という1だけの問題であるなら
※マークのない myclass:: だけではポインタであるとコンパイラが判別できないわけで、
その辺はプログラマのほうがコンパイラに譲歩してやる必要があるとでも言っておけばよいかも。

メンバ関数へのポインタって多用する?
俺なら virtual にして暗黙に vtbl/vptr 経由で使えばそれでいいと思う。

引用返信 編集キー/
■69688 / inTopicNo.4)  Re[2]: クラステンプレートのメンバー関数ポインタ用部分特殊化
□投稿者/ キム (2回)-(2014/01/21(Tue) 09:36:10)
No69660 (επιστημη さん) に返信
お返事ありがとうございます。
ご推察の通り、やりたいことは関数ポインタと任意の型/個数の引数をすべて保持して、後でoperator()により呼び出すことなのです。
スレッドに関数に渡したり、コンテナに入れて別スレッドで逐次実行したりします。
function関連を使えば簡単に実現できそうですね。

残念ながら今回は縛りが厳しいので、すべて自作で結局スマートポインタまで実装してしまいました。
一応の解決を見たので、簡単に構造を書きます。おかしな点があればご指摘ください。

まず、一番の下っ端にFuncHolderクラステンプレートがあります。
class FuncHolder<typename>;
引数の型/数(最大6)/メンバー関数かどうかによって部分特殊化していて、このクラスが関数ポインタと任意の型/個数の引数をすべて保持します。
class FuncHolder<R (C::*)(T1, T2, T3)> : public FuncHolderBase<R> {...};
そして、関数を呼び出せるように、operator()をオーバーロードしています。
今回の質問は、これのテンプレート引数の記述方法でした。

その上にいるのがFuncHolderBase<typename R>クラステンプレートで、FuncHolderはこれを継承しています。
このクラスは抽象クラスで、コンテナに入れるためだけに用意しました。
内容は virtual R operator()() const = 0;だけです。

そして、一番外側にいるのがFunction<typename R>クラスです。(今回の質問の後で追加しました)
FuncHolderBase及びFuncHolderは外から見えないようにこのクラスのprivateなインナークラスとして記述しました。
このクラスのSetメソッドは関数テンプレートになっており、FuncHolderの特別バージョンを生成します。
そして、生成したFuncHolderの特別バージョンは、FuncHolderBase<R>型のスマートポインタに保持されます。
このSetメソッドを関数テンプレートとすることで、質問内容である記述方法の簡略化を実現しました。
(コンパイラが型推定してくれるので、関数の引数に参照がなければ何も書かなくて済むようになりました)
引用返信 編集キー/
■69689 / inTopicNo.5)  Re[3]: クラステンプレートのメンバー関数ポインタ用部分特殊化
□投稿者/ キム (3回)-(2014/01/21(Tue) 09:41:36)
No69661 (774RR さん) に返信
お返事ありがとうございます。
URLまでご提示いただきありがとうございます。
おかげさまで理解はしてもらえましたが、テンプレートが嫌いなようで、理解はしたが納得はできないの一点張りでした。
上に書いたように関数テンプレートを使うことで何とか許してもらえました。

関数の引数が型推定できるパターンだと以下のように完全に省略して書けるようになりました。
Function<void> f;
f.Set(this, &MyClass::MyFunc1, 10, 1.234, _T("ABC"));
f(); // this->MyFunc1(10, 1.234, _T("ABC"));

関数の引数が型推定出来ないパターンでも関数ポインタ型は判るので問題なくなりました。
Function<int> f;
f.Set<MyClass, int&, int&>(this, &MyClass::MyFunc2, 10, 20);
int r = f(); // this->MyFunc2(10, 20);
引用返信 編集キー/
■69725 / inTopicNo.6)  Re[3]: クラステンプレートのメンバー関数ポインタ用部分特殊化
□投稿者/ キム (4回)-(2014/01/24(Fri) 17:06:12)
数日間実際に使用してもらいましたが、問題なく使えているので解決とします。
ありがとうございます。

まとめ
- クラスのインスタンスメンバー関数ポインタは通常の関数ポインタとはまったく異なり、宣言も面倒。
- クラステンプレートだとコンパイラがテンプレートパラメーターの型を自動推定できないので自分で書かなければならない。
- ヘルパー関数的な関数テンプレートを記述すれば自動推定してくれるので楽になる。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -