Gcc-8.2.0とclang-7.0.0の両方が次のコードを拒否する理由を理解できません(ライブコード here ):
_#include <array>
int main() {
constexpr std::array<int,3> v{1,2,3};
constexpr auto b = v.begin(); // error: not a constexpr
return 0;
}
_
エラーあり
_error: '(std::array<int, 3>::const_pointer)(& v.std::array<int,3>::_M_elems)'
is not a constant expression (constexpr auto b = v.begin();)
_
en.cppreference.com によると、begin()
メンバー関数はconstexpr
として宣言されます。これはコンパイラのバグですか?
std::array
を回避して、これを少し簡単にしましょう。
template <typename T, size_t N>
struct array {
T elems[N];
constexpr T const* begin() const { return elems; }
};
void foo() {
constexpr array<int,3> v{{1, 2, 3}};
constexpr auto b = v.begin(); // error
}
constexpr array<int, 3> global_v{{1, 2, 3}};
constexpr auto global_b = global_v.begin(); // ok
なぜb
はエラーなのにglobal_b
は問題ないのですか?同様に、b
がstatic constexpr
であると宣言したのに、なぜv
は大丈夫になるのでしょうか?問題は基本的にポインタに関するものです。ポインターである定数式を使用するには、常に1つの既知の定数を指す必要があります。基本的に可変アドレスを持っているため、静的ストレージ期間のないローカル変数では実際には機能しません。ただし、関数ローカルな静的またはグローバルの場合、それらは1つの定数アドレスを持っているため、それらへの定数ポインターを使用できます。
標準では、 [expr.const]/6 から:
定数式は、定数式(以下で定義)の許可された結果であるエンティティを参照するglvalueコア定数式、またはその値を持つprvalueコア定数式のいずれかです。以下の制約を満たします。
- 値がクラス型のオブジェクトの場合、[...]
- 値がポインター型の場合、静的ストレージ期間を持つオブジェクトのアドレス、そのようなオブジェクトの終わりを過ぎたアドレス([expr.add])、関数のアドレス、またはNULLポインター値、および
- [...]
b
は2番目の箇条書きにあるものではないため、これは失敗します。ただし、global_b
は太字の条件を満たします-b
がv
と宣言された場合はstatic
と同様です。