7

Consider a piece of code below. Is there an integer literal that would compile on both 32-bit and 64-bit platforms?

#include <iostream>
#include <cstdint>

void f(double)
{
    std::cout << "double\n";
}

void f(int64_t)
{
    std::cout << "int64_t\n";
}

int main()
{
    f(0L);  // works on 64-bit fails on 32-bit system
    f(0LL); // fails on 64-bit but works on 32-bit system

    f(int64_t(0));              // works on both, but is ugly...
    f(static_cast<int64_t>(0)); // ... even uglier

    return 0;
}

On platforms where int64_t is long, 0LL is a different type and overload resolution doesn't prefer it vs. double.

When int64_t is long long (including on Windows x64), we have the same problem with 0L.

(0LL is int64_t in both the 32-bit and x86-64 Windows ABIs (LLP64), but other OSes use x86-64 System V which is an LP64 ABI. And of course something portable to non-x86 systems would be nice.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Roman Byshko
  • 8,591
  • 7
  • 35
  • 57
  • You could make your own `_s64` suffix. `constexpr std::int64_t operator "" _s64(unsigned long long v) { return static_cast(v); }` You'll note that *unary minus* is applied after-the-fact. That's a C-ism that C++ inherited, and can sometimes cause subtle problems for some literal numeric edge cases. – Eljay May 31 '22 at 18:57
  • 2
    I'm tempted to say `explicit void f(double)` so integer arguments promote to int64_t but that isn't valid. Make a single templated function and check if the argument is integer or floating point with `if constexpr`? – Goswin von Brederlow May 31 '22 at 19:05
  • 2
    @GoswinvonBrederlow You can't use explicit, but one thing you can do to stop implicit conversions is to add to the overload set `template ret_type func_name(T) = delete;`. If this is a better match, i.e, wouldn't need an implicit conversion, then a compiler error is raised – NathanOliver May 31 '22 at 19:30
  • @NathanOliver The idea wasn't to delete but to make the uint64_t version the better match when the argument is ambiguous. The `f(0LLU)` wouldn't try to convert to double. – Goswin von Brederlow May 31 '22 at 20:33

1 Answers1

8

You can make a custom user defined literal for int64_t like

constexpr int64_t operator "" _i64(unsigned long long value) 
{ 
    return static_cast<std::int64_t>(value);
}

and then your function call would become

f(0_i64);

This will give you an incorrect value if you try to use the literal -9223372036854775808 for the reason stated here

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
NathanOliver
  • 171,901
  • 28
  • 288
  • 402