web-dev-qa-db-ja.com

テンプレート引数としてのconstexpr関数パラメーター

物事がどのように機能するかについてもう少し理解するために、c ++ 11を使用していくつかのおもちゃのコードで遊んでいます。この間に私は以下に簡略化する次の問題に遭遇しました:

template <int x, int y>
class add {
public:
    static constexpr int ret = x + y;
};

constexpr int addFunc(const int x, const int y) {
    return add<x,y>::ret;
}

int main() {
    const int x = 1;
    const int y = 2;
    cout << add<x,y>::ret << endl; // Works
    cout << addFunc(1,2) << endl;  // Compiler error
    return 0;
}

私はGCC 4.8.1を使用しており、出力は次のとおりです。
「x」は「int」型のテンプレート引数の定数式ではありません
「y」は、「int」型のテンプレート引数の定数式ではありません

正確に計算しようとしている2つの方法の違いは何ですかadd::ret?これらの値は両方とも、コンパイル時に使用できる必要があります。

23
Danny

addFuncはconstexprになることをコンパイラに伝えます。しかし、それはconstexpr自体ではないパラメーターに依存するため、コンパイラーはすでにそれを抑制しています。それらをconstとマークすることは、関数本体でそれらを変更しないことを意味し、関数に対して行う特定の呼び出しは、この時点では考慮されません。

addFuncにコンパイル時の定数を渡すだけであることをコンパイラーに理解させる方法があります。パラメーターをテンプレートパラメーター自体にします。

template <int x, int y>
constexpr int addFunc() {
    return add<x,y>::ret;
}

次に、

cout << addFunc<1,2>() << endl;
10
Raoul Steffen

目的がコードを少し短くするだけの場合は、C++ 14で変数テンプレートを作成できます。

template <int x, int y>
constexpr int addVar = x + y;

cout << addVar<5, 6> << endl; // Works with clang 3.5, fails on GCC 4.9.1

GCC 5 これもサポートします

10
Anton Savin

コンパイラーは、xとyがコンパイル時に常に定数値(式)として使用できるかどうか、およびC++ 11/14はconstexpr関数パラメーターをサポートしていないを認識しないため、方法はありません。 xとyは、addFuncのテンプレートadd <>のパラメーターとして使用できます。

7
fjanisze

constexpr関数の関数パラメーターは定数式ではありません。関数は外部に対してconstexprですが(これを呼び出すと定数式になる可能性があるため)、内部の計算は通常の関数の場合と同じようにconstexprです。

テンプレート引数には定数式が必要です。これらは、コードで満たされず、コンパイラエラーを生成する定数式の重要な要件です([expr.const]/2、強調版):

conditional-expressionは、コア定数式のいずれかを含まない限り、評価される可能性のある部分式(3.2)としての以下[…]:

—に適用されない限り、左辺値から右辺値への変換(4.1)

  • 不揮発性のconstオブジェクトを参照する整数型または列挙型のglvalue 前の初期化あり、定数式で初期化済みまたは
  • 不揮発性オブジェクトを参照するリテラルタイプのglvalue constexprで定義、またはそのようなオブジェクトのサブオブジェクトを参照する、または
  • 定数式で初期化された、その有効期間が終了していない不揮発性一時オブジェクトを参照するリテラル型のglvalue。

パラメーターに左辺値から右辺値への変換を適用して、それらをテンプレート引数として渡します。
最初の箇条書き項目は、関数パラメーターが事前に初期化されていないか、定数式で初期化されていないことがわかっているため適用されません。 constexpr)。

4
Columbo