質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

216閲覧

template の書き方

fana

総合スコア11668

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

0クリップ

投稿2024/04/26 07:08

編集2024/04/26 07:37

※アバウトすぎる質問タイトルについて:
以下の質問内容を適切に表現するタイトルが思いつかないため,仮のものになっています.「こうすべき」という良いタイトル案があればご指摘頂きたく.


なんとなく,このようなコード↓を書きました.

C++

1//T と,Tのメソッドの 2// 戻り値の型 : RetType 3// 引数の型 : ArgType 4// メソッド名 : Method 5//を template引数とする. 6template< 7 class T, class RetType, class ArgType, 8 RetType (T::*Method)(ArgType) 9> 10class XXX 11{ 12public: 13 RetType CallTheMethod( T &t, ArgType arg ) 14 { return (t.*Method)( arg ); } 15};

これを使うテストコードは,例えばこんな↓です.

C++

1//何かてきとーにこんなclassがあるとして… 2struct Test 3{ 4 void F( int i ){ std::cout << i << "\n"; } 5}; 6 7//こんなコードを書くと, 8// t.F(33); 9//という呼び出しが実施される. 10Test t; 11XXX<Test, void, int, &Test::F > xxx; 12xxx.CallTheMethod( t, 33 );

ここまでは良いのですが,
ここで,メソッドの引数を複数個にしたい,すなわち,上記 template の class ArgType のところを,class ...Args のようにしたいのですが,その方法がわかりません.

意味合い的にはこんな↓なのですが…

C++

1template< 2 class T, class RetType, class ...Args, //← ArgType を ...Args に変更 3 RetType (T::*Method)(Args...) //← Method の引数を Args にしたい 4> 5class XXX2 6{ 7public: 8 RetType CallTheMethod( T &t, Args... args ) 9 { return (t.*Method)( args... ); } 10};

これではコンパイルが通りません.
可変長のArgs は末尾である必要があるため,↓のコンパイルエラーとなります.

error C3525: 'Args': クラス テンプレートにテンプレート パラメーター パックが含まれている場合、そのパラメーター パックはテンプレート パラメーター リストの最後に指定する必要があります

しかしながら,Args が先に無いと,RetType (T::*Method)(Args...) を書くことができないので困っています.
このような場合,どうすれば良いのでしょうか?


[追記] 自分で試したこと:

こんなことをすれば↑のコンパイルエラーから逃げることはできそうですが,無理矢理感というか… もっとやりようがあるのではないだろうか? と…

C++

1template< class T, class RetType, class ...Args > 2class XXX2Outer 3{ 4public: 5 template< RetType (T::*Method)( Args... ) > 6 class XXX2Inner 7 { 8 public: 9 RetType CallTheMethod( T &t , Args... args ) 10 { return (t.*Method)( args... ); } 11 }; 12}; 13 14// 15XXX2Outer<Test, void, int>::XXX2Inner< &Test::F > xxx2; 16xxx2.CallTheMethod( t, 33 );
melian👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

C++20 を仮定すればかなり簡単に書けると思います。 規格について指定はありますか?

cpp

1#include <iostream> 2#include <type_traits> 3#include <utility> 4 5template <auto Method> 6 requires std::is_member_function_pointer_v<decltype(Method)> 7class XXX2 { 8 public: 9 template <class T, class... Args> 10 requires std::is_invocable_v<decltype(Method), T, Args...> 11 decltype((std::declval<T>().*Method)(std::declval<Args>()...)) 12 CallTheMethod(T &t, Args... args) { 13 return (t.*Method)(args...); 14 } 15}; 16 17struct Test { 18 void F(int i, int j) { std::cout << i << " " << j << "\n"; } 19}; 20 21int main(void) { 22 Test t; 23 XXX2<&Test::F> xxx; 24 xxx.CallTheMethod(t, 33, 44); 25}

追記。 C++11 でやれること

C++11 でも出来る範囲で最も簡単なのはクラステンプレートに渡すテンプレート引数を関数ポインタの型そのままで渡すことだと思います。

まとめて渡された型を内部の補助的なテンプレート経由の型推論で分離して必要な関数を構築するという手順を取れば型を渡すときには可変長にする必要がありません。

cpp

1#include <iostream> 2#include <type_traits> 3#include <utility> 4 5template <class MethodType, 6 MethodType Method, 7 class = typename std::enable_if<std::is_member_function_pointer<MethodType>::value, void*>::type> 8class XXX { 9 template <class T, class ReturnType, class... Args> 10 struct helper { 11 static ReturnType CallTheMethod(T& t, Args... args) { 12 return (t.*Method)(args...); 13 } 14 }; 15 template <class T, class ReturnType, class... Args> 16 static constexpr helper<T, ReturnType, Args...> deduce(ReturnType (T::*)(Args...)) { 17 return helper<T, ReturnType, Args...>(); 18 } 19 20 public: 21 static constexpr auto CallTheMethod = decltype(deduce(Method))::CallTheMethod; 22}; 23 24struct Test { 25 void F(int i, int j) { 26 std::cout << i << " " << j << "\n"; 27 } 28}; 29 30int main(void) { 31 Test t; 32 XXX<void (Test::*)(int, int), &Test::F> xxx; 33 xxx.CallTheMethod(t, 33, 44); 34}

投稿2024/04/26 08:32

編集2024/04/26 10:53
SaitoAtsushi

総合スコア5466

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2024/04/26 09:06 編集

> 規格について指定はありますか? 指定…というわけではありませんが,規格が新しいほど単純に「私が付いていけません」という率が高まります. そのようなわけで, 「もう少し古い規格 (17 とか 14 とか?)の場合であればこうなる」みたいな ある程度簡単な話 というのはあるものでしょうか? というのが気になります. 上記の ある程度簡単な話 という語の意味は…… 例えば「古い規格の場合だと,とんでもなく複雑怪奇になる」みたいな話であれば諦めます ……的な意味です.
fana

2024/04/30 03:43 編集

> C++11 でも出来る範囲で最も簡単なのはクラステンプレートに渡すテンプレート引数を関数ポインタの型そのままで渡すこと ありがとうございます. これを手掛かりにいろいろと検討できそうです. → 少しばかり検討をしてみての追加質問となりますが… この C++11 版の XXX の実装を,例えば template< class T, class ...Args > auto CallTheMethod( T &t, Args... args ) -> decltype( (t.*Method)( args... ) ) { return (t.*Method)( args... ); } 等としない理由は,これだと 「呼び出しの種類(t や args の種類の組み合わせ)の分だけ CallTheMethod のオーバーロードが生成されてしまうから」 という認識であっていますでしょうか? (tの派生関係だとか,argsが型変換でいける場合とか)
SaitoAtsushi

2024/04/30 04:59

私が C++11 版として書いたコードの意図としては、質問のコードのメンバ関数 (CallTheMethod) は関数テンプレートではなくクラステンプレートのテンプレート引数と連動して型が確定するようになっているのでそれに合わせただけです。 期待しているものの仕様が質問者自身としても明瞭な言葉に出来ないようだったのでこちらで勝手に想像して書いても期待に沿えないかもしれないと考え、コードとして提示されているものの外部仕様は大きく変えない範囲で実装のテクニックの形で答えたつもりです。 ただ、一般論としても CallTheMethod は関数テンプレートにしないほうが良いとは思います。 Test::F の仮引数の型と CallTheMethod に渡した実引数の型の辻褄が合わないときにエラーメッセージがわかりやすいので。 > オーバーロードが生成 言いたいことはなんとなく察せられますが、 C++ 用語としては実体化 (instantiation) です。 オーバーロードはひとつのスコープ内でひとつの名前に複数の異なる宣言があることを言い、ひとつのテンプレートから複数の実体 (インスタンス) が生成される状況を指しません。
fana

2024/05/02 02:47

コメント頂いているのに気づくのが遅くなりました. ※自分が立てた質問でも,回答にコメントが付いた旨の通知って来ないのですね.不便だ… Visual Studio で以下のように __FUNCDNAME__ なるものを表示させてみたところ, > (tの派生関係だとか,argsが型変換でいける場合とか) で(異なる表示になったことから)異なるメンバ関数が実体化されていることが確認できました. template< class T, class ...Args > auto CallTheMethod( T &t, Args... args ) -> decltype( (t.*Method)( args... ) ) { std::cout << __FUNCDNAME__ << "\n"; (t.*Method)( args... ); }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問