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

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

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

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

Q&A

解決済

2回答

1247閲覧

C++ テンプレートクラスで多態性の実現

退会済みユーザー

退会済みユーザー

総合スコア0

C++

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

0グッド

0クリップ

投稿2018/04/01 11:50

前提・実現したいこと

C++ テンプレートクラスで多態性の実現

お世話になります。
趣味でゲームを作ろうとしているのですが、表題の件にて行き詰ったのでご教授願いたいです。

下記のように、ゲーム中にメッセージや、キャラ
の能力値等を表示するウィンドウクラスを作成したいと考えています。
ウィンドウごとに、持っている情報は、異なりますが、情報のSet・Getは全ウィンドウに共通するため、テンプレートとしました。

template<typename T>
class Window
{
int width;
int height;
T item;

void SetItem(const T& item);
const T& GetItem();
}

class MessageWindow : public Windowstd::string
{
}

class ValueWindow : public Window< int >
{
}

ウィンドウは下記のように、マネジメントするクラスを作って、一括で管理しようと考えています。

class WindowManager
{
std::vector< std::shared_ptr<Window> > windows;
}
しかし、宣言の時点で型を指定しなければコンパイルエラーになってしまい、多態性を実現できない。

何か良い方法はないでしょうか。

発生している問題・エラーメッセージ

上記のように、テンプレートを使ったクラスで多態性を実現しようとすると
宣言の時点で、型を指定しないとコンパイルエラーになる。

該当のソースコード

class WindowManager
{
↓↓型を指定していない為、コンパイルエラー
std::vector< std::shared_ptr<Window> > windows;
}

試したこと

template<typename T>
class Window
{
int width;
int height;
T item;

void SetItem(const T& item);
const T& GetItem();
}

T itemを void* itemにしようかと考えましたが
いい案といえるのでしょうか。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答2

0

おおざっぱに言えば多態には二種類あって、コンパイル時に型が確定する多態がテンプレートで、実行時の多態を扱う方法としては抽象クラスが用意されています。

この場合はコンパイル時に型が確定しないのですから、抽象クラスを用いた設計にするのが妥当な方法です。

投稿2018/04/01 12:19

編集2018/04/01 12:42
SaitoAtsushi

総合スコア5444

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

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

退会済みユーザー

退会済みユーザー

2018/04/01 15:53

回答ありがとうございました。助かりました。
guest

0

ベストアンサー

こんにちは。

T itemを void* itemにしようかと考えましたが
いい案といえるのでしょうか。

残念ながら、良くない案です。
C言語と違ってC++は型を厳密に管理する術を提供しています。その機能を捨てるということはプログラマに掛かる負担が大きく増大します。void*型を元の型へキャストしそこなってもコンパイラは教えてくれません。そして、ここがバグるとかなりデバッグは辛いです。

std::vector< std::shared_ptr<Window> > windows;

静的ポリモーフィズムでは、それを行うことが不可能です。
それを断念する代わりに、「仮想関数テーブルを引く僅かなオーバーヘッド」と「inline関数化するそれなりのメリット」を享受するようなものです。

どうしてもinline関数のメリットを得たい場合は、型を管理する基底クラスを設け、型を判定してstatic_castすると性能劣化を最小限にできます。(llvmで多数使われているテクニックです。)
std::vector<std::unique_ptr<基底クラス> >で管理できます。(あまりお勧めではないですが、もちろんstd::shared_ptrでもOKです。)

ちょっと長くなりましたが、手抜きサンプルです。(やはりなるべく動的ポリモーフィズムを使った方が良いと思います。)

C++

1#include <iostream> 2#include <string> 3#include <memory> 4#include <vector> 5 6enum class WindowKind 7{ 8 Message, 9 Value 10}; 11template<typename T> 12WindowKind getWindowKind() 13{ 14 return WindowKind::Message; 15} 16template<> 17WindowKind getWindowKind<int>() 18{ 19 return WindowKind::Value; 20} 21 22class BaseWindow 23{ 24 WindowKind mKind; 25public: 26 BaseWindow(WindowKind iKind) : mKind(iKind) { } 27 WindowKind get() const { return mKind; } 28}; 29 30template<typename T> 31class DerivedWindow : public BaseWindow 32{ 33 T mItem; 34public: 35 DerivedWindow(T const iItem) :BaseWindow(getWindowKind<T>()), mItem(iItem) { } 36 void print() const { std::cout << mItem << "\n"; } 37}; 38 39int main() 40{ 41 std::vector<std::unique_ptr<BaseWindow> > aWindowList; 42 aWindowList.emplace_back(new DerivedWindow<std::string>("Message")); 43 aWindowList.emplace_back(new DerivedWindow<int>(12345)); 44 45 for (auto& window : aWindowList) 46 { 47 switch(window->get()) 48 { 49 case WindowKind::Message: 50 { 51 auto& aMessageWindow = static_cast<DerivedWindow<std::string>&>(*window .get()); 52 aMessageWindow.print(); 53 } 54 break; 55 56 case WindowKind::Value: 57 { 58 auto& aValueWindow = static_cast<DerivedWindow<int>&>(*window .get()); 59 aValueWindow.print(); 60 } 61 break; 62 } 63 } 64}

wandbox

投稿2018/04/01 13:13

Chironian

総合スコア23272

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

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

退会済みユーザー

退会済みユーザー

2018/04/01 15:55

回答ありがとうござしました。どちらの回答にも納得でき、次のステップに進めました。より詳細な回答を頂いた方をベストアンサーにさせていただきます。
kaizen_nagoya

2018/05/15 23:36

Chironian 様、実例ありがとうございます。g++8.1.0とclang++ 6.0.0 でコンパイルしてみました。出力は 「 Message 12345 」 ですね。コンパイルスイッチは-std=c++2a -Wall g++で71756バイト、clang++で29352バイトでした。 -Os(サイズ最小)でコンパイルすると g++ 11124 , clang++16360 大きさ、速さ、保守容易性の3点で静的、動的の選択をするようにしています。この例の動的なsampleをかけたら、3点比較してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問