高效传递std :: vector

sho*_*tsy 5 c++ arguments stl vector

当C++函数接受一个std::vector参数时,通常的模式是通过const引用传递它,例如:

int sum2(const std::vector<int> &v)
{
   int s = 0;
   for(size_t i = 0; i < v.size(); i++) s += fn(v[i]);
   return s;
}
Run Code Online (Sandbox Code Playgroud)

我相信,在双解除引用此代码结果时的矢量元素被访问,这是因为CPU应该首先解除引用v读取指针的第一个元素,该指针需要再次解除引用读取第一元件.我希望在堆栈上传递矢量对象的浅表副本会更有效.这样的浅拷贝将封装指向第一个元素的指针和大小,指针引用与原始向量相同的存储区域.

int sum2(vector_ref<int> v)
{
   int s = 0;
   for(size_t i = 0; i < v.size(); i++) s += fn(v[i]);
   return s;
}
Run Code Online (Sandbox Code Playgroud)

类似的性能,但通过传递随机访问迭代器对可以实现更少的便利性.我的问题是:这个想法有什么缺陷?我希望聪明的人能够接受支付矢量参考的性能成本或处理迭代器的不便之处.

编辑:根据下面的编辑,如果我只是将建议的vector_ref类重命名为切片范围,请考虑这种情况.目的是使用具有更自然语法的随机访问迭代器对.

int*_*nt3 10

我相信当访问向量元素时,此代码会导致双重解除引用

不必要.编译器非常聪明,应该能够消除常见的子表达式.他们可以看到操作符[]不会改变'指向第一个元素的指针',所以他们不需要让CPU在每次循环迭代时从内存重新加载它.

  • 根据内存访问计算您的成本,而不是解除引用的数量.在您提出的"浅拷贝"中,您将在调用者而不是被调用者中进行内存访问,因此最终结果将是(最多)相同.实际上,如果编译器没有内联函数,它可能会更糟,因为你传递的是更大的参数. (2认同)

jal*_*alf 9

你的想法有什么问题,你已经有两个非常好的解决方案:

  • 按原样传递向量,通过值(编译器通常会消除副本)或(const)引用,并信任编译器以消除双重间接,或者
  • 传递一个迭代器对.

当然你可以说迭代器对是"不太自然的语法",但我不同意.对于习惯STL的人来说,这是完全自然的.它非常高效,使用std算法或您自己的函数,可以准确地为您提供使用该范围所需的功能.

迭代器对是一种常见的C++习惯用法,读取代码的C++程序员可以毫无问题地理解它们,而他们会对你自制的矢量包装器感到惊讶.

如果你对性能真的很偏执,那就传递一对迭代器.如果语法真的困扰你,传递向量并信任编译器.