使用编译时vector和字符串相关的上下文变量
在大部分C语言程序中,我见过不少人是这样使用常量字符串的:
#define STR "Hello, C++"
然后可能传递进入一些函数做一些可能跟字符串本身存储字符无关的运算。但是C++中,除非控制编译流程,实在不建议使用宏。那有没有代替品呢?有!我们可以使用一些编译时就可以确定的东西传递:
#include <iostream>
static constexpr const char* ready_calc = "string";
constexpr size_t gain_const_string() {
return std::string(ready_calc).size();
}
或者:
constexpr auto check_sum(){
std::vector<int> vec{1, 2, 3, 4, 5};
return std::accumulate(std::begin(vec), std::end(vec), 0);
}
constexpr 说明符声明可在编译时求值的变量或函数。在 C++20 之前,这仅限于用文字值初始化的对象或有限约束内的函数。C++17 允许稍微扩展用途,而 C++20 进一步扩展了它。从 C++20 开始,STL 字符串和向量类现在具有 constexpr 限定的构造函数和析构函数,允许在编译时调用它们。这也意味着必须在编译时释放为字符串或向量对象分配的内存。
安全的比较整数
典中典例子:
int x{ -3 };
unsigned y{ 7 };
if(x < y) puts("true");
else puts("false");
它会打印 false。
问题是 x 是有符号的,而 y 是无符号的。标准化行为是将有符号类型转换为无符号以进行比较。这似乎违反直觉,不是吗?事实上,将无符号值转换为相同大小的有符号值,因为有符号整数使用二进制补码表示法(使用最高有效位作为符号)。给定相同大小的整数,最大有符号值是无符号值的一半。使用此示例,如果整数是 32 位,-3(有符号)将变为 FFFF FFFD(十六进制),或 4,294,967,293(无符号十进制),不小于 7。
这下看懂,那有没有办法避免这种抽象错误呢?有:
#include <utility>
int main() {
int x{ -3 };
unsigned y{ 7 };
if(cmp_less(x, y)) puts("true");
else puts("false");
}
这个玩意好说:实际上就是模板转化一下:
template< class T, class U >
constexpr bool cmp_less( T t, U u ) noexcept
{
using UT = make_unsigned_t<T>;
using UU = make_unsigned_t<U>;
if constexpr (is_signed_v<T> == is_signed_v<U>)
return t < u;
else if constexpr (is_signed_v<T>)
return t < 0 ? true : UT(t) < u;
else
return u < 0 ? false : t < UU(u);
}
<=>
:)
Spaceship运算符!哈哈,这个东西用来化简比较运算符的
三向比较的工作方式不同。它返回三种状态之一。如果操作数相等,宇宙飞船运算符将返回等于 0 的值;如果左侧操作数小于右侧操作数,则返回负值;如果左侧操作数大于右侧运算符,则返回正值。
const int a = 7; const int b = 42; static_assert((a <=> b) < 0);
返回的值不是整数。它是
<compare>
标头中的一个对象,与 0 进行比较。 如果操作数具有整数类型,则运算符从
<compare>
库中返回一个 strong_ordering 对象。
- strong_ordering::equal // 操作数相等
- strong_ordering::less // lhs 小于 rhs
- strong_ordering::greater // lhs 大于 rhs
如果操作数具有浮点类型,则运算符返回 partial_ordering 对象:
- partial_ordering::equivalent // 操作数相等
- partial_ordering::less // lhs 小于 rhs
- partial_ordering::greater // lhs 大于 rhs
- partial_ordering::unordered // 如果操作数无序
这些对象旨在使用常规比较运算符(例如,(a <=> b) < 0)与文字零 (0) 进行比较。这使得三向比较的结果比传统比较更精确。 如果这一切看起来有点复杂,那没关系。它的真正威力在于它作为对象的统一比较运算符的应用。
当然还可以自己定义:
struct Frac {
long n;
long d;
constexpr Frac(int a, int b) : n{a}, d{b} {}
constexpr double dbl() const {
return static_cast<double>(n) /
static_cast<double>(d);
}
constexpr auto operator<=>(const Frac& rhs) const {
return dbl() <=> rhs.dbl();
};
constexpr auto operator==(const Frac& rhs) const {
return dbl() <=> rhs.dbl() == 0;
};
};
版权归原作者 charlie114514191 所有, 如有侵权,请联系我们删除。