web-dev-qa-db-ja.com

constexpr std :: arrayの始まり

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として宣言されます。これはコンパイラのバグですか?

28
linuxfever

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は問題ないのですか?同様に、bstatic constexprであると宣言したのに、なぜvは大丈夫になるのでしょうか?問題は基本的にポインタに関するものです。ポインターである定数式を使用するには、常に1つの既知の定数を指す必要があります。基本的に可変アドレスを持っているため、静的ストレージ期間のないローカル変数では実際には機能しません。ただし、関数ローカルな静的またはグローバルの場合、それらは1つの定数アドレスを持っているため、それらへの定数ポインターを使用できます。


標準では、 [expr.const]/6 から:

定数式は、定数式(以下で定義)の許可された結果であるエンティティを参照するglvalueコア定数式、またはその値を持つprvalueコア定数式のいずれかです。以下の制約を満たします。

  • 値がクラス型のオブジェクトの場合、[...]
  • 値がポインター型の場合、静的ストレージ期間を持つオブジェクトのアドレス、そのようなオブジェクトの終わりを過ぎたアドレス([expr.add])、関数のアドレス、またはNULLポインター値、および
  • [...]

bは2番目の箇条書きにあるものではないため、これは失敗します。ただし、global_bは太字の条件を満たします-bvと宣言された場合はstaticと同様です。

48
Barry