16

私は C++ の新constexpr機能を見てきましたが、その必要性を完全には理解していません。

たとえば、次のコード:

constexpr int MaxSize()
{
    ...

    return ...;
}

void foo()
{
    int vec[MaxSize()];
}

次のように置き換えることができます:

int MaxSize()
{
    ...

    return ...;
}

static const int s_maxSize = MaxSize();

foo()
{
    int vec[s_maxSize];
}

アップデート

2 番目の例は、実際には標準の ISO C++ ではありませんが (これを指摘してくれた複数のユーザーに感謝します)、特定のコンパイラ (gcc など) がサポートしています。したがってconst、プログラムが有効であるということではなく、gcc がこの非標準機能をサポートしているという事実です。(私の知る限り、これは配列が関数またはメソッドに対してローカルとして定義されている場合にのみ可能です。グローバル配列のサイズはコンパイル時にまだわかっている必要があるためです。) options なしでコンパイルすると-std=c++98 -pedantic-errors、コード

int MaxSize()
{
    return 10;
}

void foo()
{
    int vec[MaxSize()];
}

gcc でコンパイルします。

そのため、これまでに寄せられたフィードバックを考慮して、私の質問を言い換えてみます (また、その間に行ったいくつかの追加の読書も)。

constキーワードを多用しています。を使用constすると、有効期間全体で特定の値を持つ定数を定義できます。定数は任意の式で初期化できます。式は一度評価されます。つまり、定数の作成時に評価されます。これらの場合、それはかなり役に立たないと思います。constexpr定数値を定義する式が実行時ではなくコンパイル時に計算されるという点で、非常に小さな最適化が導入されます。複雑な初期化を伴う実行時定数が必要になるたびに、キーワード を使用しconstます。

そのconstexprため、コンパイル時に定数を初期化する必要がある状況で役立つ場合があります。1 つの例はベクトル定義です。標準では、実行時のサイズの定義はサポートされていません。もう 1 つの例は、1 つ以上の型以外のパラメーターを持つテンプレートです。

そのような場合、私は通常マクロを使用します。

#define MAX_SIZE (10)

void foo()
{
    int vec[MAX_SIZE];
}

しかし、私の理解が正しければ、関数は定義内constexprで関数の再帰呼び出しを許可するため、マクロよりも強力です。constexprしかし、このような複雑な計算を使用してコンパイル時の定数を定義したいと思った実用的なアプリケーションは思いつきません。

したがって、たとえそれが興味深い機能であったとしても、それが必要かどうか (つまり、マクロが十分でない状況をどのくらいの頻度で解決できるか) はまだ疑問に思っています。マクロでは解決できない実際の例をいくつか見てみると、この意見を変えるのに役立つかもしれません。

4

6 に答える 6

21

int vec[s_maxSize];2番目の例では実際には違法であるため、C++では実行できません。しかし、最初の例は完全に合法的なC++0xです。

だからあなたの答えがあります。C++で提案したことを実際に行うことはできません。これは、C++0xでのみ実行できますconstexpr

また、このコードはC++0xでも機能することを指摘しておきます。これをC++で行うには、いくつかの非常に優れたクラステンプレートが必要になります。

constexpr unsigned int gcd(unsigned int const a, unsigned int const b)
{
   return (a < b) ? gcd(b, a) : ((a % b == 0) ? b : gcd(b, a % b));
}

char vec[gcd(30, 162)];

もちろん、C ++ 0xでは、ifステートメントの代わりに三項演算子を使用する必要があります。しかし、それは機能し、C++で強制的に使用するテンプレートバージョンよりもはるかに理解しやすいです。

template <unsigned int a, unsigned int b>
class gcdT {
 public:
   static unsigned int const value = gcdT<b, a % b>::value;
};

template <unsigned int a>
class gcdT<a, 0> {
 public:
   static unsigned int const value = a;

};

char vec[gcdT<30, 162>::value];

そしてもちろん、C ++ではgcd、実行時に物事を計算する必要がある場合でも、実行時に変化する引数でテンプレートを使用できないため、関数を作成する必要があります。また、C ++ 0xには、関数の結果が渡された引数によって完全に決定されることを知るという追加の最適化ブーストがあります。これは、C++のコンパイラ拡張機能でのみ表現できる事実です。

于 2011-06-25T05:25:00.053 に答える
7

マクロやテンプレートではできない constexpr でできることは、コンパイル時に文字列を解析/処理することです。上記のリンクからの小さな抜粋として、constexpr を使用すると、次のようなコードを記述できます。

#include "my_constexpr_string.h"
int main()
{
   using namespace hel;
   #define SDUMP(...) static_assert(__VA_ARGS__, "")

   SDUMP(tail("abc") == "bc");
   SDUMP( append("abc", "efgh") == "abcefgh" );
   SDUMP( prepend("abc", "efgh") == "efghabc" );
   SDUMP( extract<1,3>("help") == "el" );
   SDUMP( insert<1>("jim", "abc") == "jabcim" );
   SDUMP( remove("zabzbcdzaz", 'z') == "abbcdazzzz" );
   SDUMP( erase("z12z34z5z", 'z') == "12345"  );
   SDUMP( map("abc", ToUpper()) == "ABC" );
   SDUMP( find("0123456777a", '7') == 7 );
   SDUMP( isort("03217645") == "01234567");  
}

これが役立つ場合の例として、リテラル文字列で指定された特定のパーサーと正規表現有限状態マシンのコンパイル時の計算/構築を容易にすることができます。また、コンパイル時にプッシュできる処理が多ければ多いほど、実行時に行う処理は少なくなります。

于 2011-07-24T15:23:42.903 に答える
3
int MaxSize() {
    ...

    return ...; }

static const int s_maxSize = MaxSize();

int vec[s_maxSize];

いいえ、できません。それは正当な C++03 ではありません。可変長配列を割り当てることができるコンパイラ拡張機能があります。

于 2011-06-25T11:11:24.277 に答える
1

constexprがコンパイル時に未定義の動作を検出できるようにするもう 1 つの巧妙なトリックは、非常に便利なツールのように見えます。リンクした質問から抜粋した次の例では、SFINAEを使用して、加算がオーバーフローを引き起こすかどうかを検出しています。

#include <iostream>
#include <limits>

template <typename T1, typename T2>
struct addIsDefined
{
     template <T1 t1, T2 t2>
     static constexpr bool isDefined()
     {
         return isDefinedHelper<t1,t2>(0) ;
     }

     template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
     static constexpr bool isDefinedHelper(int)
     {
         return true ;
     }

     template <T1 t1, T2 t2>
     static constexpr bool isDefinedHelper(...)
     {
         return false ;
     }
};


int main()
{    
    std::cout << std::boolalpha <<
      addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
    std::cout << std::boolalpha <<
     addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
    std::cout << std::boolalpha <<
      addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}

その結果(ライブで見る):

true
false
true
于 2014-01-27T03:46:01.860 に答える
-4

その理由により、一般的に定数は必要ありません#define。インライン関数などはありません。

constexpr多くのキーワードと同様に、のポイントは、意図をより適切に表現できるようにすることです。これにより、コンパイラーは、ユーザーが伝えていることだけでなく、必要なものを正確に理解できるため、舞台裏でより適切な最適化を行うことができます。

この例では、コピーして何度も貼り付けるプレーンテキストの代わりに、ベクトルサイズを計算するための保守可能な関数を記述できます。

于 2011-06-25T05:13:31.407 に答える