web-dev-qa-db-ja.com

`constexpr`と` const`の違い

constexprconstの違いは何ですか?

  • そのうちの1つだけをいつ使用できますか?
  • いつ使用できますか、またどちらを選択すればよいですか。
482
MBZ

基本的な意味と構文

両方のキーワードは、オブジェクトと関数の宣言で使用できます。 objectsに適用した場合の基本的な違いは次のとおりです。

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

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

functionsに適用される場合、基本的な違いは次のとおりです。

  • constは、一般的な関数ではなく、非静的メンバー関数にのみ使用できます。メンバー関数が非静的データメンバーを変更しないことを保証します。

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

    • 関数本体は、非仮想で非常に単純でなければなりません。typedefと静的なアサートを除き、returnステートメントは1つだけ許可されます。コンストラクターの場合、初期化リスト、typedef、および静的アサートのみが許可されます。 (= default= deleteも許可されています。)
    • C++ 14以降、ルールはより緩和され、それ以降はconstexpr関数内で許可されます:asm宣言、gotoステートメント、case以外のラベルを持つステートメントdefault、try-block、非リテラル型の変数の定義、静的またはスレッド保存期間の変数の定義、初期化が実行されない変数の定義。
    • 引数と戻り値の型はリテラル型でなければなりません(つまり、一般的に言えば、非常に単純な型、通常はスカラーまたは集約)

定数式

上記のように、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として宣言しても、必ずしもコンパイル時に評価されるとは限りません。 使用可能などですが、実行時に評価される他の場所でも使用できます。

    • オブジェクトmay定数式での使用に適合withoutconstexprと宣言されます。例:

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

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

だからいつ実際にconstexprを使わなければならないのか?

  • 上記のNのようなobjectは、定数式として使用できますwithoutconstexprと宣言されます。これは、次のすべてのオブジェクトに当てはまります。

    • const
    • 整数型または列挙型and
    • それ自体が定数式である式で宣言時に初期化されます

    [これは§5.19/ 2によるものです。定数式には、[…]整数型または列挙型のglvalue […]以外の左辺値から右辺値への変更を含む部分式を含めることはできません。以前は、これはすべてのリテラル型に当てはまると主張していました。]

  • functionが定数式での使用に適合するためには、must明示的に宣言される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
    }
    

いつ使用できますか/両方を使用する必要がある場合、constconstexprtogether?

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

constexpr const int N = 5;

と同じです

constexpr int N = 5;

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

static constexpr int N = 3;

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

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

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

constexpr void f();

として宣言する必要があります

constexpr void f() const;

const関数として引き続き使用できるようにするために、C++ 14の下で。

511
jogojapan

const 変数 に適用され、 はコード内での変更を防止します

constexprは、この expression コンパイル時定数値 になることをコンパイラーに指示するので、配列長、const変数への代入などの場所で使用できます。 link オリによって与えられた多くの優れた例があります。

基本的にそれらは完全に2つの異なる概念であり、一緒に使用することができます(そしてそうすべきです)。

103
Karthik T

概要

  • constはプログラム がオブジェクトの値 を変更しないことを保証します。ただし、constは、オブジェクトがどのタイプの初期化を受けるかを保証しません。

    検討してください:

    const int mx = numeric_limits<int>::max();  // OK: runtime initialization
    

    関数max()は単にリテラル値を返します。ただし、初期化子は関数呼び出しであるため、mxはランタイム初期化を受けます。したがって、定数式として使用することはできません。

    int arr[mx];  // error: “constant expression required”
    
  • constexprは、C++ 11の新しいキーワードで、マクロとハードコードされたリテラルを作成する必要がありません。また、特定の条件下で、オブジェクトがstatic初期化を受けることを保証します。式の評価時間を制御します。 その式のコンパイル時評価 を強制することによって、constexprは、タイムクリティカルなアプリケーション、システムプログラミング、テンプレート、そして一般的に言えば、依存するすべてのコードにおいて重要な真のconstant式を定義します。コンパイル時定数.

定数式関数

定数式関数constexprとして宣言された関数です。その本体は非仮想でなければならず、typedefやstatic assertとは別に、単一のreturn文だけで構成されていなければなりません。その引数と戻り値はリテラル型でなければなりません。これは非定数式の引数と共に使用できますが、それが行われたときの結果は定数式ではありません。

定数式関数は、パフォーマンスや型の安全性を犠牲にすることなく、macroshardcoded literalsを置き換えることを目的としています。

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

定数式オブジェクト

constant-expression objectconstexprと宣言されたオブジェクトです。定数式、または定数式引数を持つ定数式コンストラクターによって構成された右辺値で初期化する必要があります。

定数式オブジェクトは、使用前に初期化が必要で、初期化子が定数式でなければならないことを除いて、constとして宣言されているかのように動作します。したがって、定数式オブジェクトは常に別の定数式の一部として使用できます。

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

定数式コンストラクタ

constant-expressionコンストラクタは、constexprと宣言されたコンストラクタです。メンバー初期化リストを持つことができますが、typedefや静的アサーションとは別に、その本体は空でなければなりません。その引数はリテラル型を持たなければなりません。

コンストラクタの引数がすべて定数式である場合、定数式コンストラクタを使用すると、コンパイラはコンパイル時にオブジェクトを初期化できます。

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

constexprについてのScott MeyersによるEffective Modern C++からのヒント:

  • constexprオブジェクトはconstであり、コンパイル中に既知の値で初期化されます。
  • constexpr関数は、コンパイル時に値がわかっている引数を指定して呼び出されると、コンパイル時に結果を生成します。
  • constexprオブジェクトおよび関数は、非constexprオブジェクトおよび関数よりも広範囲のコンテキストで使用できます。
  • constexprは、オブジェクトまたは関数のインターフェースの一部です。

出典: セキュリティ、パフォーマンス、およびC++でのカプセル化を改善するためのconstexprの使用

56
zangw

Bjarne Stroustrupによる「C++プログラミング言語第4編集」の本によると
const :「この値は変更しないことをお約束します」(7.5)を意味します。これは主にインターフェースを指定するために使用されるので、データが変更されることを恐れずにデータを関数に渡すことができます。
コンパイラは、constによる約束を守ります。
constexpr :大体「コンパイル時に評価される」という意味です(10.4)。これは主に定数を指定するために使われます。
例えば:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

関数を定数式、つまりコンパイラによって評価される式で使用できるようにするには、 constexpr を定義する必要があります。
例えば:

constexpr double square(double x) { return x∗x; }


constexprになるためには、関数はかなり単純でなければなりません:単に値を計算するreturnステートメントです。 constexpr関数は、定数でない引数に使用できますが、それが行われたときの結果は定数式ではありません。定数式を必要としないコンテキストでは、定数式以外の引数を使ってconstexpr関数を呼び出すことができます。そのため、同じ式を2回定義する必要はありません。
いくつかの場所では、定数式は言語の規則(例えば、配列の範囲(2.2.5、§7.3)、ケースラベル(2.2.2、§9.4.2)、いくつかのテンプレート引数(例: §25.2)、およびconstexprを使って宣言された定数)それ以外の場合、コンパイル時の評価はパフォーマンスにとって重要です。性能の問題とは無関係に、不変性(不変の状態をもつオブジェクト)の概念は、設計上の重要な事項です(§10.4)。

28
Mustafa Ekici

constconstexprはどちらも変数と関数に適用できます。それらは互いに似ていますが、実際には非常に異なる概念です。

constconstexprはどちらも、初期化後に値を変更できないことを意味します。だから、例えば:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

constconstexprの主な違いは、それらの初期化値がわかっている(評価される)時間です。 const変数の値はコンパイル時と実行時の両方で評価できますが、constexprは常にコンパイル時に評価されます。例えば:

int temp=Rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

値がコンパイル時または実行時にわかっているかどうかを知ることの重要な利点は、コンパイル時定数が必要なときはいつでもコンパイル時定数を使用できるという事実です。たとえば、C++では、可変長のC配列を指定することはできません。

int temp=Rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

つまり、ということです。

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

そのためconst変数は両方を定義できます 時定数をコンパイルする 配列サイズを指定するために使用できるsize1のように 実行時定数 size2のように、実行時にだけ知られていて、配列サイズを定義するのに使用することができません。一方、constexprは常に配列サイズを指定できるコンパイル時定数を定義します。

constconstexprはどちらも関数にも適用できます。 constキーワードは、constキーワードを適用してもメソッドがそのメンバ(非静的)フィールドの値を変更できないことを意味するメンバ関数(メソッド、演算子)である必要があります。例えば。

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

constexprは別の概念です。それはコンパイル時に評価されることができる関数として関数(メンバまたは非メンバ)をマークします コンパイル時定数が引数として渡された場合。例えばこれを書くことができます。

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,Rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,Rand())' can't be evaluated at compile time.

ちなみにconstexpr関数は、定数でない引数が渡された場合でも呼び出すことができる通常のC++関数です。しかしその場合、あなたはnon-constexpr値を得ています。

int value1=func_constexpr(10,Rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,Rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,Rand()) can't be evaluated at compile time.

constexprはメンバ関数(メソッド)、演算子そしてコンストラクタにも適用できます。例えば。

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

もっと 'クレイジーな'サンプル。

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.
20
Timmy_A

@ 0x499602d2がすでに指摘したように、constは初期化後に値が変更できないことを保証するだけです。ここでconstexpr(C++ 11で導入された)は変数がコンパイル時定数であることを保証します。
(LearnCpp.comの)次の例を考えてください。

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime
7
Lokesh Meher

const int varは実行時に動的に値に設定することができ、一度その値に設定されるとそれ以上変更することはできません。

constexpr int varは実行時に動的に設定することはできませんが、むしろコンパイル時に設定します。そして一度それがその値に設定されると、それはもはや変更できません。

これは堅実な例です。

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

上記のスニペットはうまくコンパイルされていて、エラーの原因となっているものはコメントアウトしています。

3
typelogic

まず第一に、どちらもc ++の修飾子です。 constで宣言された変数は初期化する必要があり、将来変更することはできません。したがって、通常、constとして宣言された変数は、コンパイル前でも値を持ちます。

ただし、constexprの場合は少し異なります。

Constexprの場合、プログラムのコンパイル中に評価できる式を指定できます。

明らかに、constexperとして宣言された変数は、constのように将来変更することはできません。

0