web-dev-qa-db-ja.com

constexpr vs. static const:どちらを好むか?

次のような整数型のコンパイル時定数を定義する場合(関数およびクラススコープで)、どの構文が最適ですか?

static const int kMagic = 64; // (1)
constexpr int kMagic = 64;    // (2)

(1)はC++ 98/03コンパイラでも機能しますが、代わりに(2)は少なくともC++ 11を必要とします。 2つの間に他の違いはありますか?現代のC++コードではどちらか一方を優先すべきですか?その理由は?


[〜#〜] edit [〜#〜]

Godbolt's CE でこのサンプルコードを試しました。

int main()
{
#define USE_STATIC_CONST
#ifdef USE_STATIC_CONST
  static const int kOk = 0;
  static const int kError = 1;
#else
  constexpr int kOk = 0;
  constexpr int kError = 1;
#endif
  return kOk;
}

static constの場合、これはGCC 6.2によって生成されたアセンブリです。

main::kOk:
        .zero   4
main::kError:
        .long   1
main:
        Push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret

一方、constexprの場合:

main:
        Push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 0
        mov     DWORD PTR [rbp-8], 1
        mov     eax, 0
        pop     rbp
        ret

どちらの場合も-O3で同じ(最適化された)アセンブリを取得しますが:

main:
        xor     eax, eax
        ret

編集#2

私はこの簡単なコードを試しました (イデオンでのライブ)

#include <iostream>
using namespace std;

int main() {
    const int k1 = 10;
    constexpr int k2 = 2*k1;
    cout << k2 << '\n';
    return 0;
}

const int k1は、constexpr int k2の計算に使用されるため、compile-timeで評価されることを示しています。

ただし、doublesにはdifferent動作があるようです。そのための別の質問を作成しました here

36
Mr.C64

のコンパイル時定数の宣言について話している限り スカラー 整数型または列挙型の場合、const(クラススコープのstatic const)とconstexprを使用してもまったく違いはありません。

コンパイラは、定数式でstatic const intオブジェクト(定数初期化子で宣言された)をサポートする必要があることに注意してください。つまり、コンパイラは、そのようなオブジェクトをコンパイル時定数として扱う以外に選択肢がありません。さらに、そのようなオブジェクトがODRで使用されていない限り、定義は不要です。これは、ランタイム値として使用されないことをさらに示しています。

また、定数初期化のルールは、ローカルstatic const intオブジェクトが動的に初期化されるのを防ぎます。つまり、そのようなオブジェクトをローカルで宣言することによるパフォーマンスの低下はありません。さらに、静的な初期化の順序付け問題に対する積分staticオブジェクトの耐性は、言語の非常に重要な機能です。

constexprは、C++で最初にconstを介して定数初期化子で実装された概念の拡張および一般化です。整数型の場合、constexprconstがすでに行ったことよりも特別なものを提供しません。 constexprは、初期化子の「安定性」の早期チェックを実行するだけです。ただし、constexprはその目的のために特別に設計された機能であるため、スタイルに適していると言うかもしれません。

17
AnT

constexpr変数は、コンパイル時に使用可能な値を持つことが保証されています。一方、static constメンバーまたはconst変数は、コンパイル時の値またはランタイム値のいずれかを意味します。 constexprと入力すると、コンパイル時の値の意図をconstよりもはるかに明示的に表現できます。

もう1つ、C++ 17では、constexpr静的データメンバー変数もインラインになります。つまり、static constexpr変数の行外定義は省略できますが、static const変数は省略できません。


コメントセクションでの要求として、関数スコープのstatic constに関する詳細な説明を以下に示します。

関数スコープのstatic const変数はほとんど同じですが、自動保存期間の代わりに、静的保存期間があります。つまり、何らかの方法で変数をグローバルとして宣言することと同等ですが、関数内でのみアクセス可能です。

static変数は関数の最初の呼び出しで初期化されるのは事実ですが、constでもあるため、コンパイラは値をインライン化し、変数を完全に最適化しようとします。したがって、関数では、ifこの特定の変数の値はコンパイル時に既知であり、コンパイラーはほとんどの場合それを最適化します。

ただし、関数スコープのstatic constの値がコンパイル時に不明な場合、silently関数を作成します(非常に値を初期化する必要があるため、低速です実行時関数が最初に呼び出されたとき。さらに、関数が呼び出されるたびに値が初期化されるかどうかを確認する必要があります。

これがconstexpr変数の利点です。コンパイル時に値がわからない場合は、遅い関数ではなくコンパイルエラーです。コンパイル時に変数の値を決定する方法がない場合は、コンパイラーがそのことを通知し、それについて何かを行うことができます。

32