3

constexpr auto v = static_cast<std::uint64_t>(1) << 32; is not ideal, because of the tedious syntax and the cast which is semantically indirect. From this thread, I learned constexpr auto v = UINT64_C(1) << 32; However, the precise semantics of the macro is

expands to an integer constant expression having the value specified by its argument and the type uint_least64_t.

Therefore, it's not exactly uint64_t. I'm wondering what is the best/proper way to define uint64_t constants.

Note that unsigned long long doesn’t necessarily map to uint64_t.

Update

I'm reluctant to use functional/C-style cast, because some (e.g., Google C++ Coding Style) say it's from C, and modern C++ should avoid using them. Looks like I should have my own opinion on this, instead of blindly following others.

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • 3
    How about good old: const uint64_t one = 1ULL; – Jeremy Friesner Feb 21 '18 at 15:51
  • 5
    Surely this is solved by not using auto? Show me the benefit that `auto` gave you, over simply declaring `std::uint64_t v = 1;` ? Anyway, you can decorate your 1 with U and L, to make it unsigned and long. – Gem Taylor Feb 21 '18 at 15:51
  • @GemTaylor Updated question. – Lingxi Feb 21 '18 at 16:00
  • 2
    Why not a function style cast then? `constexpr auto v = std::uint64_t{1} << 32`? Goes hand in hand with Herb Sutter's almost always auto advice. – StoryTeller - Unslander Monica Feb 21 '18 at 16:09
  • 1
    @Lingxi: "*Note that unsigned long long doesn’t necessarily map to uint64_t.*" And `uint64_t` doesn't have to be defined by your compiler either, yet your code assumes that it is. I see no difference between that assumption and the assumption that `ULL` is 64-bits. Are there real systems that provide `uint64_t` which *don't* define `ULL` to be 64-bits? – Nicol Bolas Feb 21 '18 at 16:14

2 Answers2

3

Therefore, it's not exactly uint64_t. I'm wondering what is the best/proper way to define uint64_t constants.

If you want to be absolutely sure, you can write uint64_t(1) (possibly std::-qualified) instead as a shorter alternative to your static_cast.

For practical purposes, UINT64_C is fine though. Implementations aren't required to define uint64_t if they don't provide any type which meets its requirements. It makes sense because of that to define UINT64_C in terms of uint_least64_t.

But on implementations where uint64_t does exist -- and you're implicitly already assuming you're using such an implementation -- uint64_t and uint_least64_t must have the exact same range, are it would pretty much require malice on the implementor's part to not make them the exact same type.

P.S. Since this is tagged language-lawyer: there is no guarantee that uint64_t(1) << 32 still has type uint64_t. Hypothetically, an implementation may provide a larger-than-64-bit int type, in which case uint64_t would be promoted to int.

  • I'm somewhat restrained from using functional style cast, because some say it's from C, and modern C++ should avoid using it (like Google's C++ Coding Style). In practice, I find functional style and C-style casts quite convenient and elegant in certain contexts. I guess I'm going to have my own opinion on this instead of blindly following others. – Lingxi Feb 21 '18 at 17:24
-2

There are specifiers for exactly this, using L makes your integer literal a long, LL a long long and ULL an unsigned long long, or, uint64_t (most of the time).

Or just drop auto all together and just let the compiler make the implicit cast.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122