constexpr関数が定数式であるかどうかを決定するものは何ですか?

Nov 30 2020

(私が知る限り、使用されているコンパイラはc ++ 17のgccです(ビジュアルスタジオでこれを見つけるのは難しいです))

#include <iostream>

using namespace std;

void increment( int& v )
{
    ++v;
}

int constexpr f()
{
    int v = 0;
    increment( v );
    return v;
}

int main( )
{
    cout << f( ) << '\n';
}

上記のコードはコンパイル時にエラーを出します:

constexpr関数 'f'は定数式にはなりません。

私が理解しているように、これは関数incrementがconstexprではないためです。私を混乱させるのは、次のコードが正常にコンパイルされることです。

#include <iostream>

using namespace std;

void increment( int& v )
{
    ++v;
}

int constexpr f()
{
    int v = 0;
    for( int i = 0; i < 1; ++i )
    {
        increment( v );
    }   
    return v;
}

int main( )
{
    cout << f( ) << '\n';
}

このコードは機能的に同じであり、インクリメントはまだconstexprではありませんが、コンパイルされます。[0、1)の範囲を通るforループによって、コンパイラーが関数がf実際にはconstexprであることを認識できる可能性があることを理解していません。

誰かがc ++のconstexprとこの明らかな矛盾についていくつかの洞察を与えることができれば、私はそれを大いに感謝します。

回答

12 aschepler Nov 30 2020 at 22:23

[dcl.constexpr] / 6によると、どちらのプログラムも「不正な形式で診断は不要」です。

デフォルトでもテンプレートでもないconstexpr関数またはconstexprコンストラクターの場合、関数またはコンストラクターの呼び出しがコア定数式の評価された部分式になるような引数値が存在しない場合、またはコンストラクターの場合は、定数で初期化されたオブジェクト([basic.start.static])の初期化完全式。プログラムの形式が正しくないため、診断は不要です。

gccが2番目のプログラムの問題に気付かないのは少し奇妙ですが、それでも準拠しています。

fたとえば、実際に定数式を必要とするコンテキストで使用された場合、診断が必要になることに注意してくださいconstexpr int n = f();

constexpr関数では許可されないものもあります。関数が定数式で使用されていない場合でも、これらには診断(通常はエラーメッセージ)が必要です。cigienの回答を参照してください。しかし、問題のプログラムは、これらのより厳しい規則のいずれにも違反していません。

6 cigien Nov 30 2020 at 22:27

f定数式を呼び出していないので、質問は、定数式で呼び出すことができないものを、その定義のみに基づいて診断する必要があるかどうかを尋ねfています

上の要件定義constexpr機能が列挙され、ここで:

constexpr関数の定義は、次の要件を満たす必要があります。

(3.1)その戻り値の型(もしあれば)はリテラル型でなければならない。

(3.2)そのパラメータ型のそれぞれはリテラル型でなければならない。

(3.3)コルーチンであってはなりません。

(3.4)関数がコンストラクタまたはデストラクタである場合、そのクラスは仮想基本クラスを持たないものとします。

(3.5)その機能本体は囲んではならない

(3.5.1)gotoステートメント、

(3.5.2)識別子ラベル、

(3.5.3)非リテラル型または静的またはスレッドの保存期間の変数の定義。

ご覧のとおり、の定義はfリスト内のどの要件にも違反していません。したがって、これを診断しないことを選択した場合、コンパイラは準拠しています。

ascheplerの回答で指摘されているconstexprようにf、そのような関数は定数式で呼び出すことはできませんが、そのように診断することはできません。これは、形式が正しくなく、診断が不要であると見なされます。

2 MarshallClow Nov 30 2020 at 22:22

fコンパイル時に実際に「呼び出し」ているわけではありません。

メイン関数が含まれている場合:static_assert(f() == 1, "f() returned 1");「f()は定数式ではありません」というエラーが発生する可能性があります。

これが関連する質問です

Sneftel Nov 30 2020 at 22:12

この標準では、constexpr関数がコンパイル時に実際にパラメータの一部のセットについて評価可能である必要がありますが、すべてではありません。コンパイラconstexprは、状況によってはコンパイル時ではない可能性のある特定のことを実行している関数を診断する必要はありません。また、そのような関数にそのようなパラメータのセットがあるかどうかも診断する必要はありません。これにより、停止問題を解決する必要がなくなります。