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

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

ただいまの
回答率

88.64%

c++:テンプレートテンプレートを関数に渡したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 334

otyaken

score 9

前提

データを保存しておくクラスSettingを作成し,データを渡す相手クラスによって処理を変更するような処理を実現したいです.

これを実現する例として,静的ポリモーフィズムを利用し,テンプレートクラスTypeを受け取り,それを表示するような基底クラスBaseと派生クラスDerivedを作成しました.

/**
 * 静的ポリモーフィズム基底クラス
 */
template<class Type, template<class>class Derived>
class Base{
    public:
        static void print(Type data){
            Derived<Type>::print(data);
        }
};

/**
 * 静的ポリモーフィズム派生クラス
 */
template<class Type>
class Derived : Base<Type, Derived>{
    public:
        static void print(Type data){
            std::cout << data << std::endl;
        }
};

表示するデータはSettingクラスを作成して保存します.
SettingクラスにはデータをBaseクラスに渡して表示するような関数Setting::printを作成しました.

/**
 * データを保存しておくクラス
 */
template<class Type>
class Setting{
    public:
        Type data;

    public:
        /**
         * データをBaseクラスに渡して出力する関数
         */
        template<template<class>class Derived>
        void print(){
            Base<Type, Derived>::print(data);
        }
};

実装したいこと

このSettingクラスを別の関数に渡し.Setting::print関数を呼び出すようにしたいのですが,コンパイルエラーが発生してしまいました.

/**
 * これはNG
 */
template<class Type, template<class>class Derived>
void NGprint(Setting<Type> &setting){
    setting.print<Derived<Type>>();
}
int main(){
    /**
     * Settingクラスの初期化
     */
    Setting<double> setting;
    setting.data = 1.0;

    NGprint<double, Derived>(setting);
}

試したこと

これらのクラスを使って,Settingクラスが定義されている関数内からSetting::print関数を呼び出すことができました.

/**
 * main関数
 */
int main(){
    /**
     * Settingクラスの初期化
     */
    Setting<double> setting;
    setting.data = 1.0;

    setting.print<Derived>();
}

また,関数を渡すときにTypeをあらかじめ指定すればコンパイルできました.

template<template<class>class Derived>
void OKprint(Setting<double> &setting){
    setting.print<Derived>();
}

void main(){
    /**
     * Settingクラスの初期化
     */
    Setting<double> setting;
    setting.data = 1.0;

    OKprint<Derived>(setting);
}

質問内容

どのように修正をすればよいでしょうか.
また,よりよい実装方法をご存じでしたら,ご教授願います.

コード全文

#include <cstdio>
#include <iostream>

/**
 * クラスの前方宣言
 */
template<class Type>
class Setting;

template<class Type, template<class>class Derived>
class Base;

template<class Type>
class Derived;


/**
 * int型を保存しておくクラス
 */
template<class Type>
class Setting{
    public:
        Type data;

    public:
        /**
         * dataを使用するクラス
         */
        template<template<class>class Derived>
        void print(){
            Base<Type, Derived>::print(data);
        }
};

/**
 * 静的ポリモーフィズム基底クラス
 */
template<class Type, template<class>class Derived>
class Base{
    public:
        static void print(Type data){
            Derived<Type>::print(data);
        }
};

/**
 * 静的ポリモーフィズム派生クラス
 */
template<class Type>
class Derived : Base<Type, Derived>{
    public:
        static void print(Type data){
            std::cout << data << std::endl;
        }
};

/**
 * これはNG
 */
template<class Type, template<class>class Derived>
void NGprint(Setting<Type> &setting){
    setting.print<Derived<Type>>();
}
/**
 * main関数
 */
int main(){
    /**
     * Settingクラスの初期化
     */
    Setting<double> setting;
    setting.data = 1.0;

    NGprint<double, Derived>(setting);
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

こんにちは。

これで行けるようです。

template<class Type, template<class>class Derived>
void NGprint(Setting<Type> &setting){
    setting.template print<Derived>();
}

setting::print()はテンプレート・メンバー関数なので template 指定が必要になります。
また、この文脈ではDerived<Type>は実体化されてクラスになっており、テンプレートではないようです。
それをテンプレート・テンプレート・パラメータへ渡すのはダメということと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/27 16:37 編集

    いただいたプログラムで実行することができました.ありがとうございます.
    まだまだ勉強が足りていないみたいです.

    質問なのですが,すべてのテンプレートメンバー関数を呼び出す際にtemplate指定が必要ということなのでしょうか.

    キャンセル

  • 2019/10/27 16:44

    関数テンプレート `NGprint` の定義に先んじて `Setting` クラスは宣言されていてメンバ関数 `print` がテンプレートであることは明らかに見えますが、 `Setting` には特殊化が追加される可能性 (そこでは `print` はテンプレートではないかもしれない) もあります。 `NGprint` の定義時点では `setting.print` がテンプレートであるかどうかは判断できません。 なので template キーワードで明示する必要があります。

    もし setting がクラステンプレートではないのであればそのような可能性はないのでメンバがテンプレートであることは確定し、 template キーワードで修飾しなくてもテンプレートであることがわかります。 なので `template` キーワードは必要はありません。

    "2 phase lookup" でウェブ検索すれば具体的なルールは知ることが出来るはずです。

    キャンセル

  • 2019/10/27 16:47

    私も正確には分かっていないのですが、テンプレート・パラメータに依存するテンプレート・メンバー関数を呼び出す際に必要になると理解しています。
    なんとなくコンパイラにとっての「誤解の余地」をなくすための指定だろうと思っています。

    キャンセル

  • 2019/10/27 16:53

    ありがとうございます.

    キャンセル

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

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る