Search code examples
c++g++32bit-64bit

Unintuitive behavior of overloaded explicit constructors


The following code compiles warning-less on a 64-bit Linux system using (native 64-bit) g++-10:

#include <cstdint>

class A {
  public:
    explicit A(unsigned long long int x) { };
    explicit A(uint64_t x) { };
};

int main() {}

However, on a 32-bit system, (same Linux distro, same version, but it is a 32-bit machine), I get this error:

t.cc:6:14: error: ‘A::A(uint64_t)’ cannot be overloaded with ‘A::A(long long unsigned int)’
    6 |     explicit A(uint64_t x) { };
      |              ^
t.cc:5:14: note: previous declaration ‘A::A(long long unsigned int)’
    5 |     explicit A(unsigned long long int x) { };

I tested the bitsizes experimentally (printf-ing sizeof(long long unsigned int)), and yes they are all 8 byte long on both 32 and 64 bit systems.

Why it then does not compile on a 32-bit system?

Extension:

I hunted a little bit for that, and I found in the glibc headers /usr/include/x86_64-linux-gnu/bits/types.h (or /usr/include/i386-linux/bits/types.h) that their defition differs by an __extension__ macro or attribute:

#if __WORDSIZE == 64
typedef signed long int __int64_t;
typedef unsigned long int __uint64_t;
#else
__extension__ typedef signed long long int __int64_t;
__extension__ typedef unsigned long long int __uint64_t;
#endif

I think this __extension__ is the important thing. If __WORDSIZE == 64, thus we are on a 64-bit environment, then these types are compatible. If not, then the type definitions differ by an __extension__. Where is this attribute (macro) from? How to make fair platform-independent code?


Solution

  • uint64_t is not a fundamental type. It is an optional "type" that is an alias to a unsigned integer type that is 64 bits wide. On your 32 bit system, that means most likely that they have it defined like

    using uint64_t = unsigned long long int; 
    

    so they are they same type, and you can't have two overloads with the same signature

    On your 64 bit machine, most likely they use unsigned long int instead since that is 64 bits on 64 bit linux distros. That's why it works for 64 bits but not 32 bits.