我一直在尝试为
<cstdint>
类型制作有符号文字简写,例如 u8、u16 等...
我开发了这样的功能:
constexpr std::int8_t operator "" _i8(unsigned long long int value){
unsigned long long int mask = 0xEF;
if(value && mask == value){
return static_cast<std::int8_t>(value);
}else{
throw std::out_of_range("");
}
}
-
实际上不是字面值的一部分。这对我来说意味着上面的代码(如果它包含 128,现在不包含)实际上对于 -128 无法正常工作。
当我在 godbolt 中尝试这个时
#include <type_traits>
#include <iostream>
#include <cstdint>
#include <bitset>
int main(){
std::cout << std::bitset<8>(static_cast<std::int8_t>(128)) << std::endl;
std::cout << static_cast<std::int32_t>(static_cast<std::int8_t>(128)) << std::endl;
std::cout << static_cast<std::int32_t>(static_cast<std::int8_t>(-128)) << std::endl;
std::cout << static_cast<std::int32_t>(-static_cast<std::int8_t>(128)) << std::endl;
return 0;
}
我得到
10000000
、-128
、-128
和128
从技术上讲,使用 128 可以在上面的示例中工作,它只会强制输出上的值为 -128,但是当他们(正确地)添加负数时,它将变成 128,而不是 int8_t。我希望 128 上没有负数错误,并且 128 上没有负数错误。
我考虑过创建某种临时对象,并为其重载
-
,并隐式转换,但即使这样,它也无法在模板函数中正常工作,因此一元 - 运算符必须返回基本类型,如果值是 128 或不是 128,我会以某种方式返回打开的基本类型,但我认为这是不可能的。
我基本上希望以下工作能够发挥作用:
template<typename T>
foo(const T& bar, const T& baz);
foo(127_i8, std::int8_t(32));
foo(-128_i8, std::int8_t(32));
这不起作用:
foo(128_i8, std::int8_t(32));
即使对于 C++20,现在要求有符号整数是二进制补码,这也是不可能的,因为不存在负整数文字这样的东西。这意味着模仿负整数文字始终是一个两步过程。如果不首先创建
-128_i8
,就无法创建 128_i8
,并且在这一点(第一步),不知道稍后是否会对其应用某种否定操作。
所以,虽然我们可以做类似this的事情来使
static_assert(-128_i8 == -128);
编译成功。
// note: this example assumes that the implementation defined
// truncation does the right thing
#include <cstdint>
struct [[nodiscard]] i8 {
[[nodiscard]]
consteval auto operator-() const noexcept -> i8
{ return {static_cast<std::int8_t>(-n)}; }
[[nodiscard]]
consteval operator std::int8_t() const noexcept
{ return n; }
std::int8_t n;
};
[[nodiscard]]
consteval auto operator"" _i8(unsigned long long int n) noexcept -> i8
{ return {static_cast<std::int8_t>(n)}; }
static_assert(-128_i8 == -128);
static_assert((128_i8).operator-() == -128);
在创建用户定义文字时,仍然无法区分
128_i8
和 -128_i8
。我们在这里能做的最好的事情就是匹配底层整数类型的截断行为。
static_assert(-128_i8 == 128_i8);
static_assert(static_cast<std::int8_t>(-128) == static_cast<std::int8_t>(128));