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

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

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

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

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

Q&A

解決済

3回答

8608閲覧

メンバ関数テンプレートを仮想関数にできないので困ってます

Chironian

総合スコア23272

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

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

0グッド

0クリップ

投稿2015/11/11 15:44

編集2016/12/22 03:09

【追記】不可能な内容かも知れないのですが、まだ少しだけ希望を持っていますので、「解決済」には致しません。もし、解をお持ちの方がいらっしゃいましたら、是非ご教授下さい。

【2016/12/22追記】
長らく放置してしまいました。断念したのでクローズします。


今、あるライブラリをC++11準拠の条件で開発してます。
コンパイラは、msvc 2015とg++4.9.2以降を想定しています。

イメージ的には、下記のlib.hを開発してユーザさんへ提供し、ユーザさんがmain.cppを開発します。
Client()関数の中身が結構大きくなりそうなので、できればClient()関数をテンプレートではなく通常の関数にしたいのです。

このような場合、通常はLib<>クラスを基底クラスから派生するようにし、Func()関数を仮想関数にすると思います。しかし、関数テンプレートを仮想関数にすることができませんので、Client()を非テンプレート化できないのです。
仮想関数に代わる何か良い方法はないでしょうか?

Typeには制限を付けられませんが、valueはあまり無茶に増えることはないので、現時点ではClient()を関数テンプレートのままとし、使用するvalueについて明示的実体化することで考えています。
しかし、もし、方法があればClient()を通常の関数で開発できるようにしたいのです。

現時点では無理でも、C++14やC++17等で対応できる可能性があればその情報を頂けるとありがたいです。

C++

1// lib.h 2#include <iostream> 3#include <typeinfo> 4 5template<int value> 6struct Lib 7{ 8 template<typename Type> 9 void Func(const Type& aInstance) 10 { 11 std::cout << value << " :" << typeid(aInstance).name() << "\n"; 12 // 実際にはここで下記シングルトンを生成しています 13 // template<typename Type, int value> 14 // class Singleton {}; 15 } 16};

C++

1// main.cpp 2#include "lib.h" 3 4class Foo {}; 5 6template<int value> 7void Client(Lib<value>* aLib) 8{ 9 aLib->Func(123); 10 aLib->Func(123.0); 11 aLib->Func(Foo()); 12} 13 14int main() 15{ 16 Lib<456> lib_456; 17 Lib<789> lib_789; 18 19 Client(&lib_456); 20 Client(&lib_789); 21}

【追記】
サンプルの説明が不適切だったので、Func()内のコメントを修正しました。

また、何のためにSingleton<Type, value>を作っているのか分からないと思います。
かなり長くなってしまうのですか、実際に使っているものに近いサンプルを以下に示します。
boostで使われているテクニックですが、シングルトン生成時、スタティック変数Instanceをこのように初期化することで、main()関数が走る前にこのシングルトンが生成されます。
これにより、Lib<>のコンストラクタで、Lib<>::Func()に渡されるTypeの型リストへアクセスできるようになるのです。
このタイミングではLib<>がコンストラクトされていないので、動的な方法ではvalueをリストできないのです。

こんなに複雑なことをしている目的は、Lib<>のインスタンスで使われているTypeのリストをvalueと関連づけて、Lib<>のコンストラクタ内で取り出せるようにすることです。

C++

1// lib.h 2#include <iostream> 3#include <typeinfo> 4#include <vector> 5 6class BaseSingleton; 7std::vector<BaseSingleton*> TypeList; 8 9class BaseSingleton 10{ 11public: 12 virtual void print(int aValue) = 0; 13}; 14 15template<typename Type, int value> 16class Singleton : public BaseSingleton 17{ 18private: 19 static Singleton& Instance; 20 Singleton() 21 { 22 std::cout << "Singleton() : " << value << " :" << typeid(Type).name() << "\n"; 23 } 24 static void use(const Singleton&) {} 25 26public: 27 static Singleton& getInstance() 28 { 29 static Singleton instance; 30 use(Instance); 31 TypeList.push_back(&instance); 32 return instance; 33 } 34 35 void print(int aValue) 36 { 37 if (aValue == value) 38 std::cout << "print() : " << value << " :" << typeid(Type).name() << "\n"; 39 } 40 41 // コピー/ムーブ禁止 42 Singleton(const Singleton&) = delete; 43 Singleton( Singleton&&) = delete; 44 Singleton& operator=(const Singleton&) = delete; 45 Singleton& operator=( Singleton&&) = delete; 46}; 47template<typename Type, int value> 48Singleton<Type, value>& Singleton<Type, value>::Instance = Singleton<Type, value>::getInstance(); 49 50template<int value> 51struct Lib 52{ 53 Lib() 54 { 55 // ここで自分で使われている型のリストを抽出してます 56 for (auto& itr : TypeList) { 57 itr->print(value); 58 } 59 } 60 61 template<typename Type> 62 void Func(const Type& aInstance) 63 { 64 Singleton<Type, value>::getInstance(); 65 66 // 他にSFINAEを使ってTypeで分岐し、下記のようなイメージの処理をします 67 // 組み込み型の時は特定の処理を行う 68 // 関数bar()を持つclassなら、bar()を呼ぶ 69 // 関数boo()を持つclassなら、boo()を呼ぶ 70 // それ以外なら、static_assert(false)する 71 } 72};

C++

1// main.cpp 2#include "lib.h" 3 4class Foo {}; 5template<int value> 6void Client(Lib<value>* aLib) 7{ 8 aLib->Func(123); 9 aLib->Func(123.0); 10 aLib->Func(Foo()); 11} 12 13class Bar {}; 14template<int value> 15void Client2(Lib<value>* aLib) 16{ 17 aLib->Func("string"); 18 aLib->Func(Bar()); 19} 20 21int main() 22{ 23 std::cout << "---------- Start main()\n"; 24 25 std::cout << "pre Lib<456>\n"; 26 Lib<456> lib_456; 27 std::cout << "post Lib<456>\n"; 28 Client(&lib_456); 29 30 std::cout << "pre Lib<789>\n"; 31 Lib<789> lib_789; 32 std::cout << "post Lib<789>\n"; 33 Client(&lib_789); 34 35 std::cout << "pre Lib<-123>\n"; 36 Lib<-123> lib_m123; 37 std::cout << "post Lib<-123>\n"; 38 Client2(&lib_m123); 39}

msvc2015での実行結果は下記の通りです。

Singleton() : 456 :int
Singleton() : 456 :double
Singleton() : 456 :class Foo
Singleton() : 789 :int
Singleton() : 789 :double
Singleton() : 789 :class Foo
Singleton() : -123 :char [7]
Singleton() : -123 :class Bar
---------- Start main()
pre Lib<456>
print() : 456 :int
print() : 456 :double
print() : 456 :class Foo
post Lib<456>
pre Lib<789>
print() : 789 :int
print() : 789 :double
print() : 789 :class Foo
post Lib<789>
pre Lib<-123>
print() : -123 :char [7]
print() : -123 :class Bar
post Lib<-123>

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

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

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

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

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

guest

回答3

0

自己解決

長い間放置してしまいました。すいません。

結局断念しました。
msvcとgccでは問題ないです。
MinGWにはコンパイル単位内のクラス数が増えすぎるとビルドできない不具合があり、それにハマると痛いのですが、最悪コンパイル単位の分割で対処できますので断念します。

投稿2016/12/22 03:08

Chironian

総合スコア23272

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

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

0

サンプルの実行結果に沿って改変してみました。
この変更でどうでしょう?

変更点は
LibBaseのFuncを特殊化で解決。
そのためClient側のchar [7] は std::stringに変更
関数boo, barの呼び分けが出来ない点は
共通のFunc関数からClient側で判断しています。

static_assertが書けない点はClientがFuncを定義していないと
ビルドエラーになるので、それを代替案としています。

C++

1#if 0 2 template<typename Type> 3 void Func(const Type& aInstance) 4 { 5 Singleton<Type, value>::getInstance(); 6 7 // 他にSFINAEを使ってTypeで分岐し、下記のようなイメージの処理をします 8 // 組み込み型の時は特定の処理を行う 9 // 関数bar()を持つclassなら、bar()を呼ぶ 10 // 関数boo()を持つclassなら、boo()を呼ぶ 11 // それ以外なら、static_assert(false)する 12 } 13#else 14 15 template<typename Type> 16 void Func(const Type& aInstance) 17 { 18 Singleton<Type, value>::getInstance(); 19 20 aInstance.Func(); 21 22 // static_assertは書けないが、Funcが無ければビルドエラーとなる。 23 } 24 25 template<> 26 void Func<int>(const int& aInstance) 27 { 28 Singleton<int, value>::getInstance(); 29 // 組み込み型の特定処理。 30 } 31 32 template<> 33 void Func<double>(const double& aInstance) 34 { 35 Singleton<double, value>::getInstance(); 36 // 組み込み型の特定処理。 37 } 38 39 template<> 40 void Func<std::string>(const std::string& aInstance) 41 { 42 Singleton<std::string, value>::getInstance(); 43 // 組み込み型の特定処理。 44 } 45 46#endif

C++

1#if 0 2class Foo {}; 3#else 4class Foo 5{ 6public: 7 void Func() const 8 { 9 boo(); 10 } 11 void boo() const 12 { 13 printf("function boo()\n"); 14 } 15}; 16#endif 17 18#if 0 19class Bar {}; 20#else 21class Bar 22{ 23public: 24 void Func() const 25 { 26 bar(); 27 } 28 void bar() const 29 { 30 printf("function bar()\n"); 31 } 32}; 33#endif

実行結果
Singleton() : -123 :class std::basic_string<char,struct std::char_traits<char>,c
lass std::allocator<char> >
Singleton() : 789 :double
Singleton() : 789 :int
Singleton() : 456 :double
Singleton() : 456 :int
Singleton() : 456 :class Foo
Singleton() : 789 :class Foo
Singleton() : -123 :class Bar
---------- Start main()
pre Lib<456>
print() : 456 :double
print() : 456 :int
print() : 456 :class Foo
post Lib<456>
function boo()
pre Lib<789>
print() : 789 :double
print() : 789 :int
print() : 789 :class Foo
post Lib<789>
function boo()
pre Lib<-123>
print() : -123 :class std::basic_string<char,struct std::char_traits<char>,c
lass std::allocator<char> >
print() : -123 :class Bar
post Lib<-123>
function bar()

投稿2015/11/12 16:41

higetarou

総合スコア57

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

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

Chironian

2015/11/13 04:05

すいません、気がつくのが遅れました。 ご回答ありがとうございます。 目的はClient()を非テンプレートにすることなので、折角ご回答頂いたのですが、狙いが異なるように思います。 SFINAEによる分岐処理は既に成功しています。
guest

0

このような場合、通常はLib<>クラスを基底クラスから派生するようにし、Func()関数を仮想関数にすると思います。

これがよく判りません。Lib<>クラスを基底クラスから派生させるために、わざわざFunc関数を仮想関数にする必要はないように思うのですが。

一応、以下のようなコードでClient関数のtemplateは外せますが、こんな単純な話ではないですか?

C++

1// 基底クラスにする 2struct LibBase 3{ 4 int value; // valueはメンバ変数にしてしまう 5 6 LibBase(int aValue) 7 : value(aValue) 8 {} 9 virtual ~LibBase() 10 {} 11 12 template<typename Type> 13 void Func(const Type& aInstance) 14 { 15 std::cout << value << " :" << typeid(aInstance).name() << "\n"; 16 // 実際にはSFINAEを使ってTypeで分岐し、下記のようなイメージの処理をします 17 // 組み込み型の時は特定の処理を行う 18 // 関数bar()を持つclassなら、bar()を呼ぶ 19 // 関数boo()を持つclassなら、boo()を呼ぶ 20 // それ以外なら、static_assert(false)する 21 } 22}; 23 24// 派生してクラステンプレート化 25template<int Value> // メンバ変数と名前がぶつかるのでちょっと変更 26struct Lib : public LibBase 27{ 28 Lib() 29 : LibBase(Value) 30 {} 31};

C++

1// 基底クラスで受け取るのでtemplate不要 2void Client(LibBase* aLib) 3{ 4 aLib->Func(123); 5 aLib->Func(123.0); 6 aLib->Func(Foo()); 7}

投稿2015/11/11 20:55

catsforepaw

総合スコア5938

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

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

Chironian

2015/11/12 00:52

ああ、ごめんなさい。丸っと大事な説明が漏れてました。 valueもFunc()の中で「静的」に処理する必要があるので、テンプレート引数にしています。 Func()の中では、Typeとvalueをテンプレート引数とするクラス・テンプレートを使っているのです。
catsforepaw

2015/11/12 05:23

やはりそうでしたか。そうなると確かに難問ですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問