15

本当に簡単

すべてのビットが設定された符号なし定数を作成するにはどうすればよいですか?

...フィールドを { } で初期化するために使用できます。

...これは、GCC 4.7.2 から -Wnarrowing 警告を受けません。

以下は満足のいくものではありません。

 struct U { unsigned ufield; };
 struct Uc { unsigned char ufield; };
 struct ULL { unsigned long long ufield; }; 
 struct U32 { unsigned ufield; }; 
 struct U64 { uint64_t ufield; }

 typedef 
    //any of the above U Uc ULL U32 U64, we will arbitrarily choose:
    U Ueg;


 // somewhere far away
 Ueg u = {-1};   // well defined by C standard, GCC 4.7.2 -Wnarrowing warning
 Ueg u = {~0U};  // finit width constant, warnings in some places, silent non-all-1s-mask others
 Ueg u = {~0ULL};  // ditto
 Ueg u = {-1ULL};  // ditto

基本的に、{} 初期化を書いているユーザーは、ufield の型を知りません。彼はそれが unsigned 型であることだけを知っていますが、幅は知りません。それがどの符号なしタイプであるかは正確ではありません。

* できるだけシンプルでエレガントな構文が必要なもう 1 つの理由 *

ここでの「ユーザー」は、実際に C または C++ プログラムを書いているわけではありません。彼は構成ファイルを編集しています。プログラム (単純な Perl または Python スクリプト) が構成ファイルを処理し、C コードを生成します。このプログラムはあまり洗練されたものではなく、現時点では次のようなテキストのチャンクを通過します。

 Foo: {-1,2,3};

typedef struct Some_Struct { unsigned a; を生成します。符号なし b、符号なし c; } Some_Struct = {-1,2,3}; //同上

基本的に、「この符号なし値のすべてのビットが設定されている」というリテラルのユーザーフレンドリーな構文を作成できるようにしたいと考えています。署名されていないものがどれほど大きいかを知る必要はありません。また、構成ファイルを処理するプログラムが複雑になりすぎることもありません。

潜在的な回答プロバイダーが、これは新しい制約であり、現実的ではないなどと文句を言わないように
します。テンプレートに関してまったく同じ問題がありました。つまり、「任意の幅の符号なし、すべて 1」のリテラルを書きたいテンプレート型の場合です。テンプレートでは、明らかにこれを行うことができる醜い、醜い、醜い構文のいくつかを実行したいと思うかもしれません: しかし、シンプルでエレガントな構文があればいいのにと思います。

*本当の質問*

Q: GCC 4.7.2 警告をトリガーせずに「すべて 1 が設定されている」定数を作成する方法はありますか?

簡単に

リテラル定数 -1 を使用して構造体のフィールドを初期化するプログラムに出くわしました。

> cat ./-1u.cpp
#include <stdio.h>

struct U { unsigned ufield; } ustruct = { -1 };

int main(int argc, char** argv)
{
   printf("ustruct.ufield    = %08x\n",ustruct.ufield);
}

以前のバージョンの GCC では警告なしでこれを受け入れていましたが、かなり最近のバージョンの GCC 4.7.2 では警告が表示されます。

> /SOME-PATH/import/gcc/gcc-4.7.2.pkg/installdir/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:3:46: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]

注: これは単なる警告です。-1 を unsigned に変換した結果は、C/C++ 標準で明確に定義されています。

> ./a.out
ustruct.ufield    = ffffffff

私は警告が嫌いなので、この迷惑な警告を黙らせたいと思います。ファイル全体に適用される #pragmas は使用しないほうがよいと思います。実際のバグに対する警告が無効になる可能性があるためです。

(ちなみに、この警告は、フィールドを初期化するときのみ表示されます。フィールド以外を初期化するときは表示されません。

unsigned u = -1;  // no cmpiler warning.

やっている

struct U { unsigned ufield; } ustruct = { ~0U };

バグを沈黙させます。

しかし、フィールドの型が unsigned ではなく uint64_t である場合、~0U は -1 とは異なる結果を提供することが指摘されました: 0xFFFFFFFFFFFFFFFF ではなく 0x00000000FFFFFFFF。(つまり、64 ビットの 1 ではなく、32 ビットの 1 です。)

構造体 U と初期化コードは完全に異なる場所に存在する可能性があり、ユーザーに通知せずにフィールドのサイズ、ビットマスクを増やすことができるようにしたいと考えています。その意図は、使用されている符号なしタイプの「すべて 1 のマスク」を取得することです。

同様に

struct U { unsigned ufield; } ustruct = { -1u };

バグを沈黙させます。(驚いたことに、-1 が unsigned と見なされる可能性があることを知りませんでした。)

しかし、有限幅の定数でもあります。

詳細

ここにテストプログラムがあります。(ちなみに、私が尋ねているのは、署名されていないメンバーを初期化するために符号付きリテラル定数 -1 を使用することだけです。他の警告は単なるテストです。64 ビットの数値がそうでないことを説明する必要はありません。 32 ビットに収まります。)

sh-3.2$ cat ./-1u.cpp 

#include <stdio.h>

unsigned um1 = -1;

unsigned un0u = ~0u;

unsigned un0ull = ~0ull;

struct Foo {
  unsigned um1;
  unsigned un0u;
  unsigned un0ull;
};

Foo foo = { -1, ~0u, ~0ull };


int main(int argc, char** argv)
{
  printf("um1    = %08x\n",um1);
  printf("un0u   = %08x\n",un0u);
  printf("un0ull = %08x\n",un0ull);

  printf("foo.um1    = %08x\n",foo.um1);
  printf("foo.un0u   = %08x\n",foo.un0u);
  printf("foo.un0ull = %08x\n",foo.un0ull);
}

sh-3.2$ /mips/proj/performance/import/gcc/gcc-4.7.2.pkg/installdir/bin/gcc -Wall ./-1u.cpp
./-1u.cpp:7:20: warning: large integer implicitly truncated to unsigned type [-Woverflow]
./-1u.cpp:15:28: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:28: warning: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:28: warning: large integer implicitly truncated to unsigned type [-Woverflow]

sh-3.2$ /mips/proj/performance/import/gcc/gcc-4.7.2.pkg/installdir/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:7:20: warning: large integer implicitly truncated to unsigned type [-Woverflow]
./-1u.cpp:15:35: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:35: warning: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:35: warning: large integer implicitly truncated to unsigned type [-Woverflow]

以前のコンパイラでは発生しません:

sh-3.2$ /usr/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:7: warning: large integer implicitly truncated to unsigned type
./-1u.cpp:15: warning: large integer implicitly truncated to unsigned type

/usr/bin/g++ --version
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-51)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
4

7 に答える 7

6

@Aliの回答のも​​う少しユーザーフレンドリーなバージョン:

#include <type_traits>

struct all_ones_type {
    template <typename T,
          typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
    constexpr operator T () const
    { return static_cast<T>(-1); }
} const all_ones;

#include <iostream>

struct X {
    unsigned short a;
    unsigned long b;
    unsigned long long c;
};

int main() {
    X x = { all_ones, all_ones, all_ones };
    std::cout << x.a << "\n"
              << x.b << "\n"
              << x.c << std::endl;
}

signed 型への変換を試みたときに何をしたいかによって、 を変更しenable_ifてすべての整数型を許可するか、 nice を使用して別のオーバーロードを追加することができますstatic_assert

于 2013-02-07T16:02:34.877 に答える
4

これはどう?署名されていないタイプでのみ機能しますが、質問には特に署名されていないと書かれています。(以下の rubenvb のコメントを参照してください。)

#include <cinttypes>
#include <iomanip>
#include <iostream>
#include <limits>
#include <type_traits>

template <typename T>
T all_bits_one() {
    static_assert(std::is_unsigned<T>::value, "the type must be unsigned");
    return std::numeric_limits<T>::max();
}

struct Ui {
    typedef unsigned int the_type;
    the_type ufield;
};

struct ULL {
    typedef unsigned long long the_type;
    the_type ufield;
};

struct U64 {
    typedef uint64_t the_type;
    the_type ufield;
};

int main() {

    using namespace std;

    Ui  ui  = { all_bits_one< Ui::the_type>() };
    ULL ull = { all_bits_one<ULL::the_type>() };
    U64 u64 = { all_bits_one<U64::the_type>() };

    cout << hex;
    cout << "unsigned int:       " <<  ui.ufield << endl;
    cout << "unsigned long long: " << ull.ufield << endl;
    cout << "unsigned int 64:    " << u64.ufield << endl;

    //all_bits_one<int>(); // causes compile-time error if uncommented

    return 0;
}

the_typeユーザーは、正確なタイプまたはそれが表されているビット数を知る必要はありません。

削除できるコードの重複がいくつかありますが、それにはコードと対処している問題をよりよく理解する必要があります。

投稿する前にコードを単純化したと思います。現状では、あなたstructのものは私には意味がありません。単純なものtypedefで十分です。

于 2013-02-07T08:51:54.897 に答える
2

すべての派手なテンプレートコードは別として、ここにいくつかの派手なC++11コードがあります。

struct Foo
{
  unsigned a;
  unsigned long b;
  unsigned long long c;
};

Foo foo = { decltype(Foo::a)(-1), decltype(Foo::b)(-1), decltype(Foo::c)(-1) };

これはエラーが発生しやすいですが、機能します。

最善の解決策は、これに(型付き)を使用することenum (class)です。

于 2013-02-07T12:43:21.173 に答える
2

タイプと一緒にマスクを提供してみませんか?

子:

 struct U { unsigned ufield; };
 #define U_MASK (-1U)

 // somewhere far away
 U u = {U_MASK};

C++:

struct U { unsigned ufield; static constexpr unsigned MASK = -1; };

 // somewhere far away
 U u = {U::MASK};
于 2013-02-07T12:00:29.783 に答える
1

別の方法

// C++03 and C++11
Ueg u = { (Ueg().ufield - 1) };

// C99 and C11 (works only inside of functions)
Ueg u = { (Ueg){0}.ufield - 1 };
于 2013-02-07T16:38:37.280 に答える
1

Ali に触発されましたが、テンプレート引数控除を使用しています。

T all_bits_one(T& dummy) { return ~(T()); }
unsigned long u = all_bits_one(u);
于 2013-02-07T15:42:10.550 に答える