370

「常に5を返す関数」を持つことは、「関数を呼び出す」という意味を壊したり、薄めたりしているように思えます。理由があるか、この機能が必要であるか、C++11にはないはずです。なんでそこにあるの?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

リテラル値を返す関数を作成し、コードレビューを思いついた場合、誰かが私に言うでしょう、それなら、return 5を書く代わりに、定数値を宣言する必要があります。

4

14 に答える 14

329

もう少し複雑なことをするとします。

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

これで、読みやすさを維持しながら、定数を数値に設定するよりも少し複雑な処理を可能にしながら、定数にまで評価できるものができました。

基本的に、何をしているかがより明確になるため、保守性を向上させるのに役立ちます。max( a, b )たとえば、次のとおりです。

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

そこでの非常に単純な選択ですがmax、定数値で呼び出すと、実行時ではなくコンパイル時に明示的に計算されることを意味します。

別の良い例はDegreesToRadians関数です。誰もがラジアンよりも読みやすいと感じています。180 度がラジアンで 3.14159265 (Pi) であることはご存知かもしれませんが、次のように書くとより明確になります。

const float oneeighty = DegreesToRadians( 180.0f );

ここにたくさんの良い情報があります:

http://en.cppreference.com/w/cpp/language/constexpr

于 2011-01-20T14:27:38.360 に答える
179

序章

constexprconstant-expressionを必要とするコンテキストで何かを評価できることを実装に伝える方法として導入されたわけではありません。準拠する実装は、C++11 より前にこれを証明できました。

実装で証明できないのは、特定のコードの意図です。

  • 開発者がこのエンティティで表現したいことは何ですか?
  • たまたま機能するという理由だけで、コードを定数式で使用することをやみくもに許可する必要がありますか?

なしでは世界はどうなるでしょうconstexprか?

ライブラリを開発していて、 interval 内のすべての整数の合計を計算できるようにしたいと考えているとします(0,N]

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

意図の欠如

コンパイラは、渡された引数が変換中にわかっている場合、上記の関数が定数式で呼び出し可能であることを簡単に証明できます。しかし、あなたはこれを意図として宣言していません - たまたまそうなっただけです。

ここで、他の誰かがやってきて、関数を読み取り、コンパイラと同じ分析を行います。「おお、この関数は定数式で使える!」、次のコードを記述します。

T arr[f(10)]; // freakin' magic

最適化

「すばらしい」ライブラリ開発者であるあなたは、fが呼び出されたときに結果をキャッシュする必要があると判断します。同じ値のセットを何度も計算したいと思う人がいるでしょうか?

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

結果

ばかげた最適化を導入することで、たまたま定数式が必要なコンテキストでの関数の使用をすべて中断しました。

関数が定数式で使用可能であると約束したconstexprことはありません。そうでなければ、そのような約束を提供する方法はありません。


では、なぜ必要なのconstexprでしょうか?

constexprの主な使用法は、インテントを宣言することです。

エンティティが - としてマークされていない場合、それは定数式constexprで使用されることを意図したものではありません。たとえそうであったとしても、そのようなコンテキストを診断するためにコンパイラに依存しています (それは私たちの意図を無視するためです)。

于 2015-03-02T23:51:17.100 に答える
94

Take std::numeric_limits<T>::max(): なんらかの理由で、これはメソッドです。constexprここで有益でしょう。

std::array別の例:別の配列と同じ大きさの C 配列 (または ) を宣言したいとします。現時点でこれを行う方法は次のようになります。

int x[10];
int y[sizeof x / sizeof x[0]];

しかし、次のように書ける方がよいのではないでしょうか:

int y[size_of(x)];

のおかげでconstexpr、次のことができます。

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}
于 2011-01-20T14:32:20.450 に答える
20

constexpr関数は本当に素晴らしく、C++ への優れた追加機能です。ただし、解決する問題のほとんどは、マクロを使用して無作法に回避できるという点で、あなたは正しいです。

ただし、 の使用法の 1constexprつには、C++03 に相当する型付き定数がありません。

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;
于 2011-01-20T16:20:05.850 に答える
14

私が読んだことから、 constexpr の必要性はメタプログラミングの問題から来ています。特性クラスには、numeric_limits::max() と考えて、関数として表される定数が含まれる場合があります。constexpr を使用すると、これらのタイプの関数をメタプログラミングで使用したり、配列の境界などとして使用したりできます。

私の頭のてっぺんから離れた別の例は、クラスインターフェイスの場合、派生型がいくつかの操作のために独自の定数を定義したい場合があるということです。

編集:

SO をいじった後、他の人がconstexprs で可能なことのいくつかの 例を思いついたようです。

于 2011-01-20T14:21:44.847 に答える
11

"Going Native 2012" での Stroustrup のスピーチから:

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human
于 2013-03-16T03:16:01.390 に答える
9

もう 1 つの用途 (まだ言及されていません) は、constexprコンストラクターです。これにより、実行時に初期化する必要のないコンパイル時定数を作成できます。

const std::complex<double> meaning_of_imagination(0, 42); 

それをユーザー定義のリテラルと組み合わせると、リテラルのユーザー定義クラスを完全にサポートできます。

3.14D + 42_i;
于 2011-01-20T18:16:59.637 に答える
7

プロジェクトを c++11 に切り替え始めたばかりで、同じ操作を実行する代替方法をクリーンアップする constexpr にとって完全に良い状況に遭遇しました。ここでの重要な点は、constexpr が宣言されている場合にのみ、関数を配列サイズ宣言に配置できるということです。私が関与しているコードの領域を進めていく上で、これが非常に役立つ状況がいくつかあります。

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}
于 2014-02-04T07:02:23.580 に答える
6

以前はメタプログラミングのパターンがありました。

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

テンプレートや特殊化、 SFINAEconstexprなどを備えた奇妙な構造を必要とせずにそのような構造を記述できるようにするために導入されたと思いますが、ランタイム関数を記述するのとまったく同じですが、結果がコンパイルで決定されることが保証されています-時間。

ただし、次の点に注意してください。

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

これをでコンパイルすると、コンパイル時に実際に評価されるg++ -O3ことがわかります。fact(10)

VLA対応コンパイラ(C99モードのCコンパイラまたはC99拡張機能を備えたC ++コンパイラ)では、次のことができる場合もあります。

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

しかし、現時点では非標準のC ++であるということはconstexpr、これに対抗する方法のように見えます(上記の場合、VLAがなくても)。そして、テンプレート引数として「正式な」定数式が必要であるという問題がまだあります。

于 2011-01-20T18:36:47.727 に答える
1

あなたの基本的な例は、定数自体と同じ議論に役立ちます。使用する理由

static const int x = 5;
int arr[x];

以上

int arr[5];

その方がずっとメンテナンスしやすいからです。constexpr を使用すると、既存のメタプログラミング手法よりもはるかに高速に読み書きできます。

于 2011-01-20T16:23:52.437 に答える
0

それはいくつかの新しい最適化を可能にすることができます。 const従来は型システムのヒントであり、最適化に使用することはできません(たとえば、constメンバー関数const_castはオブジェクトを合法的に変更constできるため、最適化を信頼することはできません)。

constexpr関数への入力がconstである場合、式は実際には定数であることを意味します。検討:

class MyInterface {
public:
    int GetNumber() const = 0;
};

これが他のモジュールで公開されている場合、コンパイラは、実装でキャストされた可能性がGetNumber()あるため、呼び出されるたびに異なる値を返さないことを信頼できません。const(明らかに、これを行ったプログラマーは撃たれるべきですが、言語はそれを許可しているので、コンパイラーは規則に従わなければなりません。)

追加constexpr

class MyInterface {
public:
    constexpr int GetNumber() const = 0;
};

コンパイラーは、の戻り値GetNumber()がキャッシュされる最適化を適用し、への追加の呼び出しを排除できるようになりました。これは、戻り値が変更されないことをより強力に保証するGetNumber()ためです。constexpr

于 2011-01-20T17:09:53.243 に答える
-3

のようなものに役立ちます

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

int some_arr[MeaningOfLife()];

これを traits クラスなどと結びつけると、非常に便利になります。

于 2011-01-20T14:28:41.540 に答える