败犬日报 2024-11-14
1. C++ iterator 为什么不定义 it.is_end()
代替 it != cont.end()
判断结束
迭代器不保存容器指针,它不知道自己属于哪个容器。因此应该定义 it.is_end(cont)
而不是 it.is_end()
,这也不是很简洁。
如果保存容器指针就会带来额外的开销(比如拷贝一个迭代器的开销增大)。
2. std::map
用 float 做为 key
浮点要考虑误差,map 用精确匹配可能找不到。
std::map
可以用 std::map::lower_bound
找 [key - eps, key + eps]
范围的数,类似这种方式进行模糊匹配。
最好不要重载比较运算符来实现模糊匹配,因为不能保证等于的传递性。
3. 函数里的 lambda,lambda 里面定义了一个 static 变量,可以保证全局唯一吗
lambda 和函数类似,需要看类型签名,相同签名 lambda 内的 static 变量就是同一个,不同就不是。有没有捕获不影响结论。
cpp
#include <cstdio>
int main() {
auto f = [] (auto y) {
static int x = 0;
x++;
printf("%d", x);
};
f(1);
f(1);
f(1.0);
f(1.0);
}
输出是 1212。
这里的 lambda 有两种签名(参数是 int 和 double),因此会有两个静态变量实例。
可以用 https://cppinsights.io/ 看看 lambda 的行为:
cpp
#include <cstdio>
int main()
{
class __lambda_3_14
{
public:
template<class type_parameter_0_0>
inline auto operator()(type_parameter_0_0 y) const
{
static int x = 0;
x++;
printf("%d", x);
}
// 此处省略一些代码
private:
template<class type_parameter_0_0>
static inline auto __invoke(type_parameter_0_0 y)
{
return __lambda_3_14{}.operator()<type_parameter_0_0>(y);
}
public:
// /*constexpr */ __lambda_3_14() = default;
};
__lambda_3_14 f = __lambda_3_14{};
f.operator()(1);
f.operator()(1);
f.operator()(1.0);
f.operator()(1.0);
return 0;
}
4. 为什么函数参数从右往左压栈
(一般情况确实是从右往左压栈)
假设 rsp 是栈指针(栈顶),压栈的参数类型都是 int64。
在大多数系统中,栈是从高地址向低地址生长的。访问栈上的局部变量或函数参数,是通过栈指针加一个数来定位。例如,通过 [rsp]
就能获取最后一个压栈的 int64,[rsp + 8]
就是倒数第 2 个。
从右往左压栈,左边的参数反而离 rsp 近,第 x 个栈上的参数是 [rsp + 8 * x]
。C 语言的变长参数(例如 printf),函数并不知道具体参数数量,但仍然可以通过 [rsp + 8 * x]
访问第 x 个参数。从左往右压栈就无法实现类似效果。