参照による `constexpr`メンバー関数の呼び出し-clang vs gcc


8

次の例を考えてみます(スニペット(0)):

struct X
{
    constexpr int get() const { return 0; }
};

void foo(const X& x)
{
    constexpr int i = x.get();
}

int main()
{
    foo(X{});
}

上記の例は、g++以前のすべてのバージョンでコンパイルされg++ 10.x、でコンパイルされることはありませんclang++。エラーメッセージは次のとおりです。

error: 'x' is not a constant expression
    8 |     constexpr int i = x.get();
      |

godbolt.orgの実例

ただしx、の本体で定数式が使用されることはないため、エラーの種類には意味がありますfoo

  • X::get()マークされてconstexprおり、の状態には依存しませんx

  • に変更const X&するconst Xと、コードは(godbolt.org上の) すべてのコンパイラスニペット(1)でコンパイルされます。


私はマークしたときには、さらに面白いX::get()static()godbolt.org上の スニペット(2) )。この変更により、g++(トランクを含む)すべてのテスト済みバージョンがコンパイルされますが、コンパイルはclang++常に失敗します。

だから、私の質問:

  • スニペット(0)のg++ 9.x受け入れは正しいですか?

  • すべてのコンパイラーがスニペット(1)を受け入れるのは正しいですか?もしそうなら、なぜ参照は重要ですか?

  • あるg++ 9.xg++ trunk受け入れることで正しいスニペット(2)


constexprは値の「マスク」であり、constは、変更されないことを通知する変数です。値は参照または参照できませんが、constは参照します。get()関数メンバーはコンパイル時に処理されます。つまり、すべての場合で0です。前に述べたように、値を参照または指定することはできません
TheArquitect

3
ここでは[expr.const] /2.11が適用されると思います。inは定数式xfooはありません。clangの正しい動作に関する古い(非公式に拒否された)バグレポートさえあります(GCCには実際のバグがありました)。
dfri


3
これについて最近書いた、関連があるようです:brevzin.github.io/c++
バリー

@Barryいいですね、あなたの記事には、対応するGCCバグレポートへの参照(拒否されたclangの1つを見つけることができました)もあり、gcc 9.xがスニペット0を受け入れるようになりました。
dfri

回答:


12

g ++ 9.xはスニペット(0)を受け入れるのに正しいですか?

番号。

すべてのコンパイラーがスニペット(1)を受け入れるのは正しいですか?もしそうなら、なぜ参照は重要ですか?

はい、そうです。

定数式は、以前の定数式の初期化を持たない、または定数式の評価中にその存続期間を開始した参照を指定するid式を使用できません。【expr.const] /2.11C ++ 20において同じ

左辺値から右辺値への変換を行わずに非参照変数に名前を付ける場合も同じではありません。x.get()x左辺値としてのみ参照され、constexpr実際xにはのメンバーにアクセスしない関数を呼び出すだけなので、問題はありません。

g ++ 9.xおよびg ++トランクはスニペット(2)を受け入れるのに正しいですか?

いいえ、式にはまだx上記のルールに違反する部分式が含まれているためです。


打ち鳴らすの関連(非公式拒否)バグレポート打ち鳴らすの同じ正確性を指摘(常に)スニペット0コンパイルに失敗
dfri
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.