constexpr と const の違いは何ですか? 質問する

constexpr と const の違いは何ですか? 質問する

constexprとの違いは何ですかconst?

  • どちらか一方だけを使用できるのはいつですか?
  • 両方をいつ使用でき、どのように選択すればよいですか?

ベストアンサー1

基本的な意味と構文

どちらのキーワードも、関数だけでなくオブジェクトの宣言でも使用できます。オブジェクトに適用する場合の基本的な違いは次のとおりです。

  • constオブジェクトを定数として宣言します。これは、一度初期化されると、そのオブジェクトの値は変更されないことが保証され、コンパイラはこの事実を最適化に利用できます。また、初期化後に変更されることを意図していないオブジェクトを変更するコードをプログラマが記述するのを防ぐのにも役立ちます。

  • constexpr標準で定数式と呼ばれるものに使用するのに適したオブジェクトを宣言します。ただし、これがconstexprこれを行う唯一の方法ではないことに注意してください。

関数に適用する場合の基本的な違いは次のとおりです。

  • const非静的メンバー関数にのみ使用でき、一般的な関数には使用できません。メンバー関数が非静的データ メンバーを変更しないことを保証します (変更可能なデータ メンバーは除きます。これらはいずれにしても変更できます)。

  • constexprは、メンバー関数と非メンバー関数の両方、およびコンストラクタで使用できます。これは、関数が定数式での使用に適していることを宣言します。コンパイラは、関数が特定の基準 (7.1.5/3,4) を満たしている場合にのみこれを受け入れます。最も重要なのは(†)です。

    • 関数本体は非仮想かつ非常に単純である必要があります。typedef と静的アサートを除き、単一のreturnステートメントのみが許可されます。コンストラクターの場合、初期化リスト、typedef、および静的アサートのみが許可されます。(ただし、= default= delete許可されます。)
    • C++14 以降では、ルールが緩和され、constexpr 関数内で許可される内容は、asm宣言、文、およびgoto以外のラベルを持つ文、try ブロック、非リテラル型の変数の定義、静的またはスレッド ストレージ期間の変数の定義、初期化が実行されない変数の定義です。casedefault
    • 引数と戻り値の型はリテラル型(つまり、一般的には非常に単純な型、通常はスカラーまたは集合体)である必要があります。

定数式

上で述べたように、constexprオブジェクトと関数の両方を定数式での使用に適したものとして宣言します。定数式は単なる定数以上のものです。

  • テンプレートパラメータや配列サイズ指定子など、コンパイル時の評価が必要な場所で使用できます。

    template<int N>
    class fixed_size_list
    { /*...*/ };
    
    fixed_size_list<X> mylist;  // X must be an integer constant expression
    
    int numbers[X];  // X must be an integer constant expression
    
  • ただし、注意してください:

  • 何かを として宣言しても、constexpr必ずしもコンパイル時に評価されることが保証されるわけではありません。そのような用途には使用できますが、実行時に評価される他の場所でも使用できます。

  • オブジェクトは、宣言されなくても定数式で使用できる場合があります例:constexpr

    int main()
    {
      const int N = 3;
      int numbers[N] = {1, 2, 3};  // N is constant expression
    }
    

    これが可能なのはN、 が定数であり、宣言時にリテラルで初期化されるため、 が宣言されていなくても定数式の基準を満たすためですconstexpr

では、実際にいつ使用する必要があるのでしょうかconstexpr?

  • 上記のようなオブジェクト、宣言せずにN定数式として使用できます。これは、次のすべてのオブジェクトに当てはまります。 constexpr
    • const そして
    • 整数型または列挙型
    • 宣言時に、それ自体が定数式である式で初期化されます。

[これは、§5.19/2 によるものです: 定数式には、「[…] 整数型または列挙型の glvalue でない限り、lvalue から rvalue への変更」を含む部分式を含めることはできません。これはすべてのリテラル型に当てはまるという私の以前の主張を訂正してくれた Richard Smith に感謝します。

  • 関数を定数式で使用できるようにするためには、明示的に宣言する必要がありますconstexpr。定数式関数の基準を満たすだけでは十分ではありません。例:

    template<int N>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }
    

const両方を一緒に使用できる場合、または使用すべきなのはいつですかconstexpr ?

A. オブジェクト宣言内。両方のキーワードが宣言される同じオブジェクトを参照する場合、これは必要ありません。constexprを意味しますconst

constexpr const int N = 5;

と同じです

constexpr int N = 5;

ただし、キーワードがそれぞれ宣言の異なる部分を参照する場合があることに注意してください。

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

NPここで、 はアドレス定数式、つまりそれ自体が定数式であるポインターとして宣言されています。 (これは、アドレス演算子を静的/グローバル定数式に適用することによってアドレスが生成される場合に可能です。) ここでは、 と の両方がconstexpr必要constです。constexprは常に宣言されている式 (ここではNP) を参照しますが、constは を参照しますint(これは const へのポインターを宣言します)。 を削除すると、const式が不正になります ((a) 非 const オブジェクトへのポインターは定数式にはならず、(b) は&N実際には定数へのポインターであるため)。

B. メンバー関数の宣言。C ++11ではconstexprを意味しますがconst、C++14とC++17ではそうではありません。C++11で次のように宣言されたメンバー関数は、

constexpr void f();

宣言する必要がある

constexpr void f() const;

C++14 では関数として使用できるようにするためconst

おすすめ記事