私は次のヘルパー関数を持っています:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(&)[N]) {
return N;
}
これは静的配列の長さを返します。過去にはこれは常に機能していましたが、私がこれを行うと:
struct Foo
{
unsigned int temp1[3];
void Bar()
{
constexpr std::size_t t = Length(temp1); // Error here
}
};
MSVS 2017を使用すると、エラーが発生します。
error C2131: expression did not evaluate to a constant note: failure was caused by a read of a variable outside its lifetime note: see usage of 'this'
私は誰かが私が間違っていることに光を当てることができることを望んでいました。
MSVCは正しいです。 Length(temp1)
は定数式ではありません。から [expr.const] p2
式eは、eの評価がない限り、コア定数式です。抽象マシンのルールに従って、次の式のいずれかを評価します。
this
、ただしe;の一部として評価されているconstexpr関数またはconstexprコンストラクターを除く。
temp1
は暗黙的にthis
を評価するため(this->temp1
を参照しているため)、定数式はありません。 gccとclangは、拡張機能としてVLAをサポートしているため、これを受け入れます(-Werror=vla
または-pedantic-errors
でコンパイルしてみてください)。
なぜこれが許可されないのですか?さて、あなたは基礎となる要素にアクセスし、潜在的にそれらを変更することができます。 constexpr
配列または定数式として評価されている配列を扱っている場合、これは完全に問題ありませんが、そうでない場合は、次の値を操作するため、定数式を使用できない可能性があります。実行時に設定されます。
Length(decltype(temp1){})
動作するようです 。
残念ながら、コメントすることはできませんが、Mehrdadの解決策は間違っています。理由:それは技術的に未定義の動作ではありませんがそれは未定義の振る舞い。 constexprの評価中に、コンパイラは未定義の動作をキャッチする必要があります。したがって、 コードの形式が正しくありません 。
あなたの質問はすでに回答済みですが、それを「修正」する方法に関しては、手っ取り早い方法は置き換えることです
_Length(temp1)
_
と
_Length(*(true ? NULL : &temp1))
_
これは技術的に未定義の動作ですが、MSVCでは実際には正常に機能すると思います。
UBにもかかわらず機能するソリューションが必要な場合は、ポインターを使用するようにLength
を変更できます。
_template<typename T, std::size_t N>
constexpr std::size_t Length(const T(*)[N]) {
return N;
}
_
その後、Length(true ? NULL : &temp1)
を使用できます。