Skip to content

败犬日报 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 个参数。从左往右压栈就无法实现类似效果。