Is there a portable literal suffix for int64_t and similar types?
Asked Answered
P

1

12

I was trying to understand std::variant:

#include <cstdint>
#include <variant>

std::variant<int64_t, double> v;

I wanted to assign an int64_t variant: v = 5L; That compiles on x86_64 because int64_t is long. But it does not compile on arm, because int64_t is long long. The type deduction has now two equal choices between int64_t and double to convert my number to, so it declines. With variant<int64_t, string> I wouldn't even have noticed the conversion, because then there is only one available and the compiler would have accepted it.

Similar issue with: v = 5LL; Now arm / 32 bit is fine, but x86_64 not anymore.

I get this compiling on both platforms but this is (sometimes) a type conversion with potential side-effects I am not able to foresee: v = int64_t(5LL);. Without the LL I wouldn't even be able to express values outside 32bit int.

The INT64_C macro seems to be the most portable and safest way to express this: v = INT64_C(5); But this not nice to read and write anymore.

Is there a similar literal suffix like L/LL for int64_t that is portable?

Portrait answered 22/12, 2020 at 10:14 Comment(7)
A simple solution would be to use a cast instead: int64_t i = (int64_t)5;. (And it's fully portable and resolved at compile time.)Explanation
Does this answer your question? UL suffix vs uint32_t castDenti
Another option could be to define a literal operator on your own: SO: Fixed-width integer literals in C++? (I could swear that I somewhere read that they might be added to the standard...)Explanation
@scheff: I didn't even expect the c-cast to work, but it seems to work. In the end I prefer not to use c-casts in c++ and would probably write static_cast<int64_t>(...) instead. But then the constructor notation int64_t(...); is nicer to the eyes, at least for me. The integer literal approach seems to be the best way to go forwards, thanks for the hint.Portrait
I prefer C-casts as I'm too old to get used with the correct C++ casts. In most cases (dynamic_cast excluded to which even I got used) it does the intended... ;-)Explanation
@kmdreko: Thanks for the hint, but in the end they just describe my problem in the answers, but not a solution. Or I missed it.Portrait
en.cppreference.com/w/cpp/utility/variant/operator%3D claims that v = 5L should always compile, because double isn't a candidate for assignment (in the example with v4). This seems to be because it is a narrowing conversion so long&& t = 5l; double x[] = { static_cast<long&&>(t) }; doesn't compileMongolian
V
5

No, there are no standard literals for fixed width integer aliases.

One potential workaround would be to use std::variant<long long, double> v;. Although long long theoretically isn't guaranteed to be exactly 64 bits (it may be wider, but not narrower), it is 64 bits on practically every system that supports long long today. The benefit is that long long of course has a standard literal. The potential drawback is that the size situation may theoretically change in future.

A more general solution is to give up on using a literal suffix, and instead use a cast: v = static_cast<int64_t>(5);.

Another solution is to create a user defined literal as shown in this answer linked in comments:

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

There is a proposal to add literals such as this to the standard library: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1280r2


On a related note, there is a proposal to add literals for std::size_t and std::ptrdiff_t. That proposal suggests core language literals instead of a standard library literals: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0330r3

Virginia answered 22/12, 2020 at 10:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.