12

C++11 では、constexpr 関数をテンプレート パラメーターの既定値として使用しています。次のようになります。

template <int value>
struct bar
{
    static constexpr int get()
    {
        return value;
    }
};

template <typename A, int value = A::get()>
struct foo
{
};

int main()
{
    typedef foo<bar<0>> type;

    return 0;
}

G++ 4.5 および 4.7 はこれをコンパイルしますが、Clang++ 3.1 はコンパイルしません。clang からのエラー メッセージは次のとおりです。

clang_test.cpp:10:35: error: non-type template argument is not a constant expression
template <typename A, int value = A::get()>
                                  ^~~~~~~~
clang_test.cpp:17:19: note: while checking a default template argument used here
        typedef foo<bar<3>> type;
                ~~~~~~~~~^~
clang_test.cpp:10:35: note: undefined function 'get' cannot be used in a constant expression
template <typename A, int value = A::get()>
                                  ^
clang_test.cpp:4:23: note: declared here
        static constexpr int get()
                             ^
1 error generated.

どちらが正しいですか?

4

2 に答える 2

13

LLVM IRC チャンネルの Richard Smith (zygoloid) がこの問題について私と短い話をしました。それがあなたの答えです

<litb> hello folks
<litb> zygoloid, what should happen in this case?
<litb> http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument
<litb> it seems to be clang's behavior is surprising
<litb> zygoloid, i cannot apply the "point of instantiation" rule to constexpr 
  function templates. if i call such a function template, the called definition's 
  POI often is *after* the specialization reference, which means at the point of 
  the call, the constexpr function template specialization is "undefined".
<zygoloid> it's a horrible mess. Clang does not do what the standard intends, but 
  as you note, the actual spec is gloriously unclear
<d0k> :(
<zygoloid> we should instantiate bar<3>::get(), because it is odr-used, but we 
  don't, because we incorrectly believe it's used in an unevaluated context
<zygoloid> conversely, the point of instantiation is too late :/
<zygoloid> PR11851

そのため、Clang は呼び出された関数テンプレートまたはクラス テンプレートのメンバー関数をインスタンス化することがありますが、それらのインスタンス化が遅すぎて呼び出しが表示されない場合があり、他のケースでは、それらを必要としないと考えているため (未評価)、それらをインスタンス化することさえありません。環境)。

于 2012-05-23T20:56:55.517 に答える
3

GCCClangは正しいと思います

n3290から引用:

14.3.2テンプレートの非型引数[temp.arg.nontype]

  1. 非型、非テンプレートのテンプレートパラメータのテンプレート引数は、次のいずれかである必要があります。
    • 整数型または列挙型の非型テンプレートパラメーターの場合、テンプレートパラメーターの型の変換された>定数式(5.19)。また
    • ..。

編集:5.19 3

リテラル定数式は、リテラル型のprvalueコア定数式ですが、ポインター型ではありません。整数定数式は、整数型またはスコープなしの列挙型のリテラル定数式です。[注:このような式は、配列の境界(8.3.4、5.3.4)、ビットフィールドの長さ(9.6)、基になる型が固定されていない場合の列挙子の初期化子(7.2)、nullポインター定数(4.10)として使用できます。 )、および配置として(7.6.2)。—end note]タイプTの変換された定数式は、リテラル定数式であり、暗黙的にタイプTに変換されます。ここで、暗黙の変換(存在する場合)はリテラル定数式で許可され、暗黙の変換シーケンスにはユーザー定義の変換のみが含まれます。値から値への変換(4.1)、統合プロモーション(4.5)、およびナローイング変換(8.5.4)以外の統合変換(4.7)。

[注:このような式は、ケース式(6.4.2)、基になる型が固定されている場合は列挙型初期化子(7.2)、整数型または列挙型の非型テンプレート引数(14.3)として使用できます。—エンドノート]

于 2012-05-23T13:51:49.963 に答える