テンプレートメタプログラミング


38

誰かが最初のテンプレートのメタプログラミング方法が無限ループになる理由を私に説明できますが、2番目の方法は正しく実行されます。

#include <iostream>
using namespace std;

template<int N, int M>
struct commondivs {                                              
  static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;
};

template<int N>
struct commondivs<N,N> {
  static const int val = N;
};


int commondiv(int N, int M){
    if(N==M){
        return N;
    }   
    return (N<M)?commondiv(N,(M-N)):commondiv((N-M),M);     
}

int main() {

    cout << commondivs<9,6>::val << endl;
    cout << commondiv(9,6) << endl;
    return 0;
}


2
目標は、テンプレートメタプログラミングを使用することでした。constexprオプションではありません。
Exxul

constexprオプションではないことを明示するためにc ++ 98タグを追加しました。(C ++ 11で導入されました)。それは既存の回答を無効にします。Exxul、制限されているC ++バージョンを明確にしてください。
MSalters

申し訳ありませんが、タグを削除しました。
Exxul

回答:


44
(N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val

この行は、コンパイル時に条件がわかっていて、分岐の1つが実行されない場合でも、commondivs<N,(M-N)>::valおよびの両方のインスタンス化を引き起こしますcommondivs<(N-M),M>::val

交換する? :std::conditional_t、この制限を持ちません、:

static const int val = std::conditional_t<N < M, commondivs<N,(M-N)>, commondivs<(N-M),M>>::val;

15

問題は、条件演算子のすべてのオペランドが評価されますので、両方commondivs<N,(M-N)>commondivs<(N-M),M>インスタンス化され、その取得valGETは再帰的なテンプレートのインスタンスに評価され、その後、リード。

if constexpr ifを適用してconstexpr staticメンバー関数に入れることができます。

値がの場合、truestatement-falseが破棄され(存在する場合)、それ以外の場合は、statement-trueが破棄されます。

template<int N, int M>
struct commondivs {                                              
  constexpr static int get_val() {
    if constexpr (N<M) return commondivs<N,(M-N)>::val; // if true, the else part won't be evaluated
    else return commondivs<(N-M),M>::val;               // vice versa
  }
  static const int val = get_val();
};

住む


評価されたか、インスタンス化されただけですか?
Daniel McLaury

@DanielMcLaury評価済み; インスタンス化されただけではありません。
songyuanyao

の値は::val必ず両方のブランチで生成する必要がありますが、これはまだ(静的なconstメンバーを持つテンプレートの)インスタンス化です。実行時に評価がないではないが起こる...よく、それはコンパイルしたことがないので、それは明らかにできない...
役に立たない

8

三項演算子は似ていませんif constexpr。コンパイラがそれを見ると、両方のブランチのコードを生成する必要があります。つまり、テンプレートをインスタンス化するためcommondivs<M, N>に、コンパイラテンプレートcommondivs<N, M - N>との両方をインスタンス化しますcommondivs<N - M, M>

それとは対照的に、commondiv(N, M - N)そしてcommondiv(N - M, M)2つの関数呼び出しに変換されます。どちらを採用するかは、関数が実際に呼び出されたときに決定されます。

添加。

HolyBlackCatはで解決策を示しましたstd::conditional_t。ここに別のものがあります:

template<int N, int M>
struct commondivs {                                              
    static constexpr int min = (N < M) ? N : M;
    static constexpr int max = (N < M) ? M : N;
    static constexpr int val = commondivs<min, max - min>::val;
};

template<int N>
struct commondivs<N, N> {
    static constexpr int val = N;
};

0

次の理由により、無限再帰が発生します。

static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;

?:@Engが言うように、はメタテンプレートプログラミングではありませんconstexpr

@HolyBlackCatの回答を確認したいとします。


1
それは役に立ちません。?:はありませんconstexpr
平均

いいえ、試します。同じ無限ループ。
Exxul
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.