web-dev-qa-db-ja.com

静的constexprintと昔ながらの列挙型:いつ、なぜ?

これは基本的な質問かもしれませんが、今のところ自分では答えがわかりません。

次のコードについて考えてみます。

template<bool b>
struct T {
    static constexpr int value = (b ? 42 : 0);
};

template<bool b>
struct U {
    enum { value = (b ? 42 : 0) };
};

int main() {
    static_assert(T<true>::value == 42, "!");
    static_assert(T<false>::value == 0, "!");
    static_assert(U<true>::value == 42, "!");
    static_assert(U<false>::value == 0, "!");
}

私はTのような構造体を使用することに慣れていますが、同じ目的(主に特性の定義)で使用されるUのような構造体を何度も見ました。

私が見る限り、どちらもコンパイル時に解決され、ほぼ同じ問題を解決しますが、TUよりもはるかに読みやすいようです(まあ、私は知っています、私の個人的な意見)。

私の質問は非常に単純です。一方のソリューションがもう一方のソリューションよりも優れている技術的な理由はありますか?
さらに、そのうちの1つが実行可能な解決策ではない場合はありますか?

25
skypjack

このように使用した場合、積分定数に目立った違いはありません。

ただし、enumは実際には優れています。これは、真の名前付き定数であるためです。 constexpr積分定数は、たとえばODRで使用できるオブジェクトであり、リンクエラーが発生します。

_#include <iostream>

struct T {
    static constexpr int i = 42;
    enum : int {x = 42};
};

void check(const int& z) {
    std::cout << "Check: " << z << "\n";
}

int main() {
    // check(T::i); // Uncommenting this will lead to link error
    check(T::x);
}
_

check(T::i)がコメント解除されている場合、プログラムはリンクできません。

_/tmp/ccZoETx7.o_:関数 `main '内:_ccc.cpp_:(_.text+0x45_):` _T::i_'への未定義の参照_collect2_:エラー:ldが_1_終了ステータスを返しました

ただし、真のenumは常に機能します。

26
SergeyA

レガシーコードだと思います。

enum { value = (b ? 42 : 0) };

c ++ 03およびC++ 11で有効なコードです。

static constexpr int value = (b ? 42 : 0);

c ++ 11でのみ有効です。

さらに、そのうちの1つが実行可能な解決策ではないケースはありますか?

どちらもC++ 11で実行可能なソリューションです。どちらを使用するかの選択は、チームによって異なります。それは政策決定の問題になるでしょう。

SergeyAによる回答 が示すように、enumは真の定数です。 ODRはできません-それらを使用してください。 ODRできます-constexprを使用します。これらのどれがアプリケーションにとって望ましいかによって、enumsとconstexprsのどちらを使用するかを決定できます。

4
R Sahu

SergeyAによって現在受け入れられている回答 はC++ 17以降保持されなくなりました( 定義とODR )。

以下を除いて、すべての宣言は定義です。

  • .。
  • (非推奨)constexpr指定子を使用してクラス内で定義された静的データメンバーの名前空間スコープ宣言
struct S {
    static constexpr int x = 42; // implicitly inline, defines S::x
};
constexpr int S::x; // declares S::x, not a redefinition

したがって、C++ 17の時点では、列挙型よりも表現力のある静的constexpr定義を使用します。

0
Touloudou