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 likeunsignedto a typedef name likell. That's only possible for cv-qualifiers, i.e.constandvolatile, not other qualifiers that change the type likeunsigned.The compiler should issue a diagnostic for this code and GCC does correctly do so with
-pedanticor-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 llwould beunsigned long longabsent documentation saying so.The documentation mentions that this syntax was allowed in K&R C for
typedefs, but not in ISO C. Presumably this is left over for K&R C compatibility wherelong longandunsigned long longdon't exist and so this syntax seems to not be implemented as expected for these types. And the C++11usingdeclaration is basically just syntactical sugar fortypedef, so the behavior was probably taken from thetypedefbehavior, even if it doesn't make sense for compatibility.-pedantic-errorsenforces 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.