为什么`std :: initializer_list`经常按值传递?

use*_*108 62 c++ c++11

几乎我在SO上看到的所有帖子都涉及到std::initializer_list,人们倾向于传递一个std::initializer_list值.根据这篇文章:

http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

如果想要传递传递的对象的副本,则应该按值传递.但复制a std::initializer_list并不是一个好主意

复制a std::initializer_list不会复制基础对象.在原始初始化程序列表对象的生存期结束后,不保证基础数组存在.

那么为什么它的一个实例经常被价值所传递,而不是通过const&哪个保证不会产生不必要的副本呢?

Kon*_*lph 56

它是通过价值传递的,因为它便宜.std::initializer_list作为一个薄的包装器,很可能被实现为一对指针,因此复制(几乎)与通过引用一样便宜.另外,我们实际上并没有执行副本,我们(通常)执行移动,因为在大多数情况下,参数无论如何都是从临时构造的.但是,这不会对性能产生影响 - 移动两个指针与复制它们一样昂贵.

另一方面,访问副本的元素可能更快,因为我们避免了一个额外的解除引用(引用的那个).

  • @ user1095108是的,标准没有指定.然而,在现实世界中没有一种神奇的"别名"类型.引用要么完全省略 - 在本地范围内我们只能引用原始实体 - 或者用指针代替.如果通过引用*将值传递给函数*(并且该函数未内联),则实际上*保证*将被指针替换. (10认同)
  • @ user1095108它确实有点保证:标准*也*按值传递`std :: initializer_list`.这是一个非常强烈的暗示.如果标准库使用按值传递,那么您可以安全地执行相同的操作,因为即使它效率低下,您仍然会受到影响.此外,该标准给出了如何实现它的明确建议(§18.9/ 2:"一对指针或一个指针加上一个长度将是`initializer_list`的明显表示."). (8认同)
  • @ user1095108从概念上讲,`initializer_list`是两个迭代器.整个STL的设计基于复制迭代器很便宜的假设.有很多参数支持传递值而不是Konrad提供的参数 - 最重要的可能是传递的值对于一个简单的析构函数是暂时的,因此可以直接构造,因为参数可能是最有说服力的 - 但不能通过价值来调用绩效考虑因素. (2认同)

Jam*_*nze 13

可能由于相同的原因,迭代器几乎总是按值传递:复制迭代器被认为是"便宜的".在这种情况下initializer_list,还有一个事实是大多数实例都是具有普通析构函数的临时实例,因此编译器可以直接在它放置函数参数的地方构造它们,而不需要复制.最后,事实上,与迭代器一样,被调用函数可能想要修改该值,这意味着如果它是通过对const的引用传递的话,它必须在本地复制它.

编辑:

只是为了概括:标准库通常假设最好按值传递迭代器,initializer_lists和函数对象.因此,您应该确保您设计的任何迭代器,iterator_lists或功能对象都很便宜,并且您应该在自己的代码中假设它们便宜复制.关于何时使用const的引用以及何时使用值的传统规则应该可以修改以反映这一点:

对于除迭代器,initializer_lists或函数对象之外的类类型,使用对const的引用传递; 否则使用pass by value.

  • 但是为什么要复制“std::function<>”实例呢?当然,复制它并不便宜。 (2认同)