28

C ++プログラミングの世界で時々出てくる一般的な質問は、コンパイル時のエンディアンの決定です。通常、これはほとんど移植性のない#ifdefで行われます。しかし、C ++ 11constexprキーワードとテンプレートの特殊化は、これに対するより良い解決策を提供しますか?

次のようなことを行うのは合法的なC++11でしょうか?

constexpr bool little_endian()
{
   const static unsigned num = 0xAABBCCDD;
   return reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD;
}

次に、両方のエンディアンタイプのテンプレートを特殊化します。

template <bool LittleEndian>
struct Foo 
{
  // .... specialization for little endian
};

template <>
struct Foo<false>
{
  // .... specialization for big endian
};

そして、次のことを行います。

Foo<little_endian()>::do_something();
4

8 に答える 8

12

constexpr (C++20 より前)を使用してコンパイル時にエンディアンを決定することはできません。reinterpret_cast[expr.const]p2 によって明示的に禁止されています。これは、共用体の非アクティブなメンバーから読み取るという iain の提案と同様です。このようなキャストは として解釈されるため、別の参照型へのキャストも禁止されていreinterpret_castます。

アップデート:

これは C++20 で可能になりました。片道 (ライブ):

#include <bit>
template<std::integral T>
constexpr bool is_little_endian() {
  for (unsigned bit = 0; bit != sizeof(T) * CHAR_BIT; ++bit) {
    unsigned char data[sizeof(T)] = {};
    // In little-endian, bit i of the raw bytes ...
    data[bit / CHAR_BIT] = 1 << (bit % CHAR_BIT);
    // ... corresponds to bit i of the value.
    if (std::bit_cast<T>(data) != T(1) << bit)
      return false;
  }
  return true;
}
static_assert(is_little_endian<int>());

(C++20 では 2 の補数の整数が保証されていることに注意してください。ビットの順序は指定されていません。そのため、データのすべてのビットが整数の予想される位置にマップされていることを確認するだけで済みます。)

ただし、C++20 標準ライブラリがある場合は、次のように質問することもできます。

#include <type_traits>
constexpr bool is_little_endian = std::endian::native == std::endian::little;
于 2011-11-19T22:34:11.870 に答える
12

N2116が組み込まれる文言であると仮定すると、あなたの例は形式が正しくありません (C++ には「合法/違法」の概念がないことに注意してください) 。[decl.constexpr]/3 の提案されたテキストは言う

  • その関数本体は { return expression; } 、式が潜在的な定数式 (5.19) である形式の複合ステートメントでなければなりません。

あなたの関数は、ローカル変数も宣言しているという点で要件に違反しています。

編集:この制限は、関数の外に num を移動することで克服できます。その場合、式は次のように定義される潜在的な定数式である必要があるため、関数はまだ整形式ではありません。

関数パラメーターのすべての出現箇所が適切な型の任意の定数式に置き換えられたときの定数式である場合、その式は潜在的な定数式です。

IOWreinterpret_cast<const unsigned char*> (&num)[0] == 0xDD は定数式でなければなりません。ただし、そうではありません:&numアドレス定数式 (5.19/4) になります。ただし、そのようなポインターの値にアクセスすることは、定数式では許可されていません。

添字演算子 [] とクラス メンバー access . and 演算子、&and*単項演算子、およびポインター キャスト (dynamic_casts、5.2.7 を除く) は、アドレス定数式の作成に使用できますが、これらの演算子を使用してオブジェクトの値にアクセスしてはなりません。

編集: 上記のテキストは C++98 のものです。どうやら、C++0x は、定数式を許可するより寛容です。式には、配列参照の左辺値から右辺値への変換が含まれます。これは、定数式から禁止されています。

定数式で初期化された非揮発性 const 変数または静的データ メンバーを参照する有効な整数型の左辺値に適用されます。

(&num)[0]const 変数を「参照」するのか、それともリテラルのみがそのような変数を「参照」するのかは明確ではありませんnum(&num)[0]その変数を参照する場合、reinterpret_cast<const unsigned char*> (&num)[0]まだ「参照」しているかどうかは不明numです。

于 2009-10-18T05:19:20.660 に答える
4

これは非常に興味深い質問です。

私は言語弁護士ではありませんが、reinterpret_cast をユニオンに置き換えることができるかもしれません。

const union {
    int int_value;
    char char_value[4];
} Endian = { 0xAABBCCDD };

constexpr bool little_endian()
{
   return Endian[0] == 0xDD;
}
于 2009-10-18T20:39:14.207 に答える
1

これは不正行為のように思えるかもしれませんが、いつでも endian.h を含めることができます... BYTE_ORDER == BIG_ENDIAN は有効な constexpr です...

于 2014-05-12T22:54:45.067 に答える
-4

あなたの目標が、little_endian()コンパイル時にコンパイラが定数 true または false に最適化されることを保証することである場合、その内容が実行可能ファイルに巻き込まれたり、実行時に実行されたりすることなく、2 つのうちの「正しい」ものからのみコードを生成します。Fooテンプレート、私はあなたが失望しているのではないかと心配しています.

私も言語の専門家ではありませんが、constexpris like inlineor register: は、最適化の可能性があることをコンパイラ作成者に警告するキーワードのように見えます。それを利用するかどうかは、コンパイラの作成者次第です。言語仕様は通常、最適化ではなく動作を義務付けています。

また、実際にさまざまな C++0x 準拠のコンパイラでこれを試して、何が起こるかを確認しましたか? で呼び出された場合にどちらを使用するかを判断できないため、それらのほとんどはデュアルテンプレートで窒息すると思いますfalse

于 2009-10-18T21:26:20.020 に答える