在 C++ 中使用 T1 = 无符号 T2 的奇怪结果

Strange result of using T1 = unsigned T2 in C++

提问人:earthmessenger 提问时间:7/2/2023 更新时间:7/2/2023 访问量:92

问:

下面的代码让我感到困惑。

https://godbolt.org/z/WcMTYM1q7

#include <iostream>

using ll = long long;
using ull = unsigned ll;

int main() {
    ull x = 0;
    std::cout << x << std::endl;
}

此代码在 g++ 11 中编译失败,但在 g++ 12 和 g++ 13 中成功编译。在 g++ 11 中,编译器说:

error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'ull' {aka 'long unsigned int'})

我还测试了以下代码。

https://godbolt.org/z/PrePfoa6E

#include <iostream>

using ll = long long;
using ull = unsigned ll;
using ull2 = std::make_unsigned_t<ll>;

int main() {
    std::cout << typeid(ll).name() << std::endl;
    std::cout << typeid(ull).name() << std::endl;
    std::cout << typeid(ull2).name() << std::endl;
}

使用 g++ 13 编译代码时,它会打印 和 ,这意味着 和 。当使用 g++ 11 编译代码时,它会打印 、 和 ,这意味着 和 。总而言之,不是预期的类型。xjylong longunsigned intunsigned long longxmylong longunsigned longunsigned long longunsigned ll

为什么会这样?是编译器错误吗?我应该什么时候使用?std::make_unsigned

C 使用 Unsigned-Integer 键入 G++

评论

0赞 BoP 7/2/2023
“我什么时候应该使用 std::make_unsigned?”每当您需要某种类型的无符号版本时。您可能会问自己,如果效果很好,为什么还要费心添加到库中呢?std::make_unsigned_t<x>unsigned x

答:

3赞 user17732522 7/2/2023 #1

using ull = unsigned ll;不是有效的标准 C++。您不能将类型说明符(如)添加到 typedef 名称(如 .这仅适用于 cv 限定符,即 和 ,而不是其他更改类型的限定符,例如 。unsignedllconstvolatileunsigned

编译器应为此代码发出诊断程序,并且 GCC 使用 或 正确地执行此操作。-pedantic-pedantic-errors

它完全没有这些标志进行编译,但不使用它们,这表明接受此声明可能是一种有意的不符合标准的行为,在这种情况下,没有理由假设没有文档说明这一点。unsigned llunsigned long long

文档提到,在 K&R C 中允许这种语法,但在 ISO C 中不允许,据推测,这是为了 K&R C 兼容性而保留的,并且不存在,因此这种语法似乎没有按预期实现这些类型。而 C++11 声明基本上只是 的语法糖 ,所以行为可能是从行为中获取的,即使它对兼容性没有意义。 强制执行标准一致性,以便不再允许此兼容性功能。typedeflong longunsigned long longusingtypedeftypedef-pedantic-errors

但是,无论哪种方式,GCC 11 的过载解决失败对我来说都是无意的,并且可以解释为更高版本中修复的错误。

2赞 Jan Schultke 7/2/2023 #2

这是一个编译器错误。 我不知道为什么 GCC 会编译这个,但绝对是格式错误的代码。 重载解决失败是允许此声明的结果,因为通常声明为:、 或 all work。 将其声明为会产生某种损坏的类型,而这些类型实际上不是其中任何一个。unsigned llullunsignedunsigned longunsigned long longunsinged ll

using ull = unsigned ll;

Clang 拒绝了它,因为它应该:

<source>:4:22: error: type-id cannot have a name
using ull = unsigned ll;
                     ^~

MSVC 也是如此:

<source>(4): error C2187: syntax error: 'll' was unexpected here

通常,只能在说明符列表中与(隐式)结合使用。您必须使用 ,否则此代码格式不正确。unsignedintstd::make_unsigned_t<ll>

您仍然可以通过添加以下标志来使 GCC 拒绝此操作:

-Wpedantic -Werror
# or
-pedantic-errors