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 的类型。他只知道它是无符号类型,但不知道它有多宽。不完全是哪种无符号类型。

* 我想要尽可能简单优雅的语法的另一个原因 *

我不妨提点别的:这里的“用户”实际上并不是在编写 C 或 C++ 程序。他正在编辑一个配置文件。一个程序,一个简单的 Perl 或 Python 脚本,处理配置文件,并生成 C 代码。这个程序不是很复杂,目前通过的文本块看起来像

 Foo: {-1,2,3};

生成 typedef struct Some_Struct { unsigned a; 无符号 b,无符号 c;} Some_Struct = {-1,2,3}; // 同上

基本上,我希望能够为“设置此无符号值中的所有位”的文字提供一种用户友好的语法。无需知道未签名的有多大。并且没有处理配置文件的程序变得太复杂。

以免潜在的答案提供者抱怨这是一个新的约束,不现实等:
我在模板方面遇到了完全相同的问题。即对于模板类型,我想在其中编写一个“无符号的任何宽度,全为 1”的文字。在模板中,我可能更愿意做一些丑陋的、丑陋的、丑陋的语法,显然可以做到这一点:但我真的希望有一个简单、优雅的语法。

*真正的问题*

问:有什么方法可以创建一个“全为 1”而不触发 GCC 4.7.2 警告的常量?

简短的

我遇到了一个程序,该程序使用文字常量 -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 转换为无符号的结果在 C/C++ 标准中得到了很好的定义:

> ./a.out
ustruct.ufield    = ffffffff

我不喜欢警告,所以我想消除这个烦人的警告。我不喜欢使用适用于整个文件的#pragmas,因为这可能会禁用对真正错误的警告。

(顺便说一句,只有在初始化字段时才会收到此警告。在初始化非字段时不会

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

正在做

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

使错误沉默。

但有人指出,如果字段的类型不是无符号的,而是 uint64_t,那么 ~0U 提供的结果与 -1 不同:0x00000000FFFFFFFF 而不是 0xFFFFFFFFFFFFFFFF。(即 32 位 1,而不是 64 位 1。)

struct U 和初始化代码可能位于完全不同的位置,我们希望能够在不通知用户的情况下增加字段的大小,即位掩码。目的是获得正在使用的任何无符号类型的“全 1 掩码”。

相似地

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

使错误沉默。(令我惊讶的是 - 我不知道 -1 可以被认为是无符号的。)

但也是一个有限宽度常数。

细节

这是一个测试程序。(顺便说一句,我要问的是使用有符号文字常量 -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;
}

根据您在尝试转换为有符号类型时想要发生的情况,您可以更改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的 s 对我来说毫无意义,一个简单的typedef就足够了。

于 2013-02-07T08:51:54.897 回答
2

为什么不提供掩码和类型?

C:

 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 回答
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 回答
1

受阿里启发,但使用模板参数推导。

T all_bits_one(T& dummy) { return ~(T()); }
unsigned long u = all_bits_one(u);
于 2013-02-07T15:42:10.550 回答
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 回答
0

也许通过将 uint 初始化为 -1 您想要的是所有位都设置为“1”?
在这种情况下:

typedef uint MyUIntType;
MyUIntType mID = (MyUIntType)~0;

~应用“1 的补码” 0,有效地翻转其所有位。
结果将是您的 uint 类型可以容纳的最大值,这在 0 是有意义的值并且需要将变量初始化为“其他”的情况下很有用。

于 2018-08-29T11:20:01.960 回答