我想要一种类型,例如unsigned char
:
但是,与unsigned char
不同,不允许使用别名。我的意思是,该类型没有[basic.lval/11.8]例外:
如果程序尝试通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
[...]
- 一种字符,无符号字符或std ::字节类型。
是否可以具有这样的类型?
原因:我几乎从不使用unsigned char
的别名属性。因此,我想改用一种类型,该类型不会阻止某些类型的优化(注意,我问这个问题是因为我实际上有一些函数,由于{ {1}})。因此,我希望有一种类型适用于此类型:“不用为不使用的东西付费”。
下面是一个示例,其中unsigned char
阻止了优化:Using this pointer causes strange deoptimization in hot loop
答案 0 :(得分:3)
标准的该部分调出char
,unsigned char
和std::byte
。但是,您可以创建自己的类型,例如std::byte
,并且不允许使用别名:
enum class my_byte : unsigned char {};
使用它并不是很好,因为您必须转换为unsigned char
才能对其进行有意义的处理。但是,您可以重载按位和算术运算符,以使其更易于使用。
我们可以使用以下简单功能进行验证:
auto foo(A& a, B& b) {
auto lhs = b;
a = 42;
auto rhs = b;
return lhs + rhs;
}
如果允许A
为B
的别名,则编译器将不得不生成两个负载:一个负载为lhs
,另一个负载为rhs
。如果不允许A
为B
的别名,则编译器可以生成单个加载并将其自身添加值。 Let's test it:
// int& cannot alias with long&
auto foo(int& a, long& b) {
auto lhs = b;
a = 42;
auto rhs = b;
return lhs + rhs;
}
// std::byte& can alias with long&
auto bar(std::byte& a, long& b) {
auto lhs = b;
a = (std::byte)42;
auto rhs = b;
return lhs + rhs;
}
// if my_byte& can alias with long&, there would have to be two loads
auto baz(my_byte& a, long& b) {
auto lhs = b;
a = (my_byte)42;
auto rhs = b;
return lhs + rhs;
}
结果如下:
foo(int&, long&):
mov rax, QWORD PTR [rsi]
mov DWORD PTR [rdi], 42
add rax, rax
ret
bar(std::byte&, long&):
mov rax, QWORD PTR [rsi]
mov BYTE PTR [rdi], 42
add rax, QWORD PTR [rsi]
ret
baz(my_byte&, long&):
mov rax, QWORD PTR [rsi]
mov BYTE PTR [rdi], 42
add rax, rax
ret
因此my_byte
与char
和std::byte
继承的别名属性不同
答案 1 :(得分:0)
您可以定义自己的类型:
#include <type_traits>
class uchar {
unsigned char value = {};
public:
template <typename T,
std::enable_if_t<
std::is_convertible_v<T, unsigned char>,
int
> = 0>
constexpr uchar(T value)
: value{static_cast<unsigned char>(value)}
{}
constexpr uchar()
{}
template <typename T,
std::enable_if_t<
std::is_convertible_v<T, unsigned char>,
int
> = 0>
constexpr uchar& operator=(T value)
{
this->value = static_cast<unsigned char>(value);
return *this;
}
explicit constexpr operator unsigned char() const
{
return value;
}
friend constexpr uchar operator+(uchar lhs, uchar rhs) {
return lhs.value + rhs.value;
}
friend constexpr uchar operator-(uchar lhs, uchar rhs) {
return lhs.value - rhs.value;
}
// And so on...
};
// The compiler could technically add padding after the `value` member of
// `uchar`, so we `static_assert` to verify that it didn't. I can't imagine
// any sane implementation would do so for a single-member type like `uchar`
static_assert(sizeof(uchar) == sizeof(unsigned char));
static_assert(alignof(uchar) == alignof(unsigned char));