The following code confuses me.
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;
}
This code fails to compile, in g++ 11, but successful to compile in g++ 12 and g++ 13. In g++ 11, compiler says:
error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'ull' {aka 'long unsigned int'})
And I also test the following code.
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;
}
When using g++ 13 to compile the code, it prints x
, j
and y
, which mean long long
, unsigned int
and unsigned long long
. And when using g++ 11 to compile the code, it prints x
, m
and y
, which mean long long
, unsigned long
and unsigned long long
. All in all, unsigned ll
is not the expected type.
Why does this happen? Is it a compiler bug? When should I use std::make_unsigned
?
using ull = unsigned ll;
is not valid standard C++. You can't add a type-specifier like unsigned
to a typedef name like ll
. That's only possible for cv-qualifiers, i.e. const
and volatile
, not other qualifiers that change the type like unsigned
.
The compiler should issue a diagnostic for this code and GCC does correctly do so with -pedantic
or -pedantic-errors
.
That it compiles at all without these flags, but doesn't with them, indicates that it is likely an intended non-standard-conforming behavior to accept this declaration and in that case there is no reason to assume that unsigned ll
would be unsigned long long
absent documentation saying so.
The documentation mentions that this syntax was allowed in K&R C for typedef
s, but not in ISO C. Presumably this is left over for K&R C compatibility where long long
and unsigned long long
don't exist and so this syntax seems to not be implemented as expected for these types. And the C++11 using
declaration is basically just syntactical sugar for typedef
, so the behavior was probably taken from the typedef
behavior, even if it doesn't make sense for compatibility. -pedantic-errors
enforces standards-conformance, so that this compatibility feature is not permitted anymore.
However, the overload resolution failure for GCC 11 looks unintended to me either way and could be explained as a bug fixed in the later versions.