C++ 为什么“std::initializer_list”经常按值传递?

C++ 为什么“std::initializer_list”经常按值传递?,c++,c++11,C++,C++11,在我看到的几乎每一篇文章中,涉及到std::initializer\u list,人们倾向于按值传递std::initializer\u list。根据这篇文章: 如果要复制传递的对象,则应按值传递。但是复制std::initializer\u列表并不是一个好主意,因为 复制std::initializer\u列表不会复制底层对象。 在的生存期之后,不能保证基础阵列存在 原始初始值设定项列表对象已结束 那么,为什么它的一个实例经常按值传递,而不是按,比如说常量&,它保证不会产生不必要的副本?它

在我看到的几乎每一篇文章中,涉及到
std::initializer\u list
,人们倾向于按值传递
std::initializer\u list
。根据这篇文章:

如果要复制传递的对象,则应按值传递。但是复制std::initializer\u列表并不是一个好主意,因为

复制
std::initializer\u列表
不会复制底层对象。 在的生存期之后,不能保证基础阵列存在 原始初始值设定项列表对象已结束


那么,为什么它的一个实例经常按值传递,而不是按,比如说
常量&
,它保证不会产生不必要的副本?

它按值传递,因为它很便宜<代码>标准::初始值设定项\u列表,作为一个薄包装,最有可能实现为一对指针,因此复制(几乎)和通过引用传递一样便宜。此外,我们实际上并没有执行复制,而是(通常)执行移动,因为在大多数情况下,参数都是由临时变量构造的。然而,这不会对性能产生影响——移动两个指针和复制它们一样昂贵


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

它是按值传递的,因为它很便宜<代码>标准::初始值设定项\u列表,作为一个薄包装,最有可能实现为一对指针,因此复制(几乎)和通过引用传递一样便宜。此外,我们实际上并没有执行复制,而是(通常)执行移动,因为在大多数情况下,参数都是由临时变量构造的。然而,这不会对性能产生影响——移动两个指针和复制它们一样昂贵


另一方面,访问副本的元素可能更快,因为我们避免了一次额外的去引用(引用的去引用)。

可能是因为迭代器几乎总是按值传递的原因:复制迭代器被认为是“便宜的”。在
初始值设定项\u list
的情况下,还有一个事实,即大多数实例都是临时性的,带有琐碎的析构函数,因此编译器可以在放置函数参数的位置直接构造它们,而不需要复制。最后,与迭代器一样,被调用函数可能希望修改该值,这意味着如果该值通过对const的引用传递,则必须在本地复制该值

编辑:

概括地说:标准库一般假设最好按值传递迭代器、初始化器列表和函数对象。因此,您应该确保您设计的任何迭代器、迭代器列表或函数对象的复制成本低,并且您应该在自己的代码中假设它们的复制成本低。关于何时使用常量引用以及何时使用值的传统规则可能应该修改以反映这一点:

对于迭代器、初始值设定项列表或函数对象以外的类类型,使用对常量的按引用传递;否则请使用传递值


可能出于同样的原因,迭代器几乎总是按值传递:复制迭代器被认为是“便宜的”。在
初始值设定项\u list
的情况下,还有一个事实,即大多数实例都是临时性的,带有琐碎的析构函数,因此编译器可以在放置函数参数的位置直接构造它们,而不需要复制。最后,与迭代器一样,被调用函数可能希望修改该值,这意味着如果该值通过对const的引用传递,则必须在本地复制该值

编辑:

概括地说:标准库一般假设最好按值传递迭代器、初始化器列表和函数对象。因此,您应该确保您设计的任何迭代器、迭代器列表或函数对象的复制成本低,并且您应该在自己的代码中假设它们的复制成本低。关于何时使用常量引用以及何时使用值的传统规则可能应该修改以反映这一点:

对于迭代器、初始值设定项列表或函数对象以外的类类型,使用对常量的按引用传递;否则请使用传递值


据我所知,编译器识别常见的“按值返回”习惯用法,并将其优化为“移动”,这实际上并不涉及其实现中的任何内存拷贝。@MatthewD我们这里不是说返回。据我所知,编译器识别常见的“按值返回”习惯用法,并将其优化为“移动”,实际上,它的实现中不涉及任何内存副本。@MatthewD,我们这里不是说返回。
const&
不一定是作为指针实现的(如果我记得标准正确的话),它也可能是某种别名。@user1095108 True,标准没有指定这一点。然而,在现实世界中,没有神奇的“别名”类型。引用要么被完全省略(在局部范围内,我们可以只引用原始实体),要么被指针替换。如果通过引用将值传递给函数(该函数未内联),则实际上可以保证该值会被指针替换。@user1095108 True。然而,仅仅通过引用访问它们一次的成本就足以抵消在传递参数时复制一个额外指针的成本(事实上,它的结果可能是相同的)。@user1095108这实际上是一种保证:该标准还通过值传递
std::initializer\u list
。这是一个非常强烈的暗示。如果标准库使用传递值,那么您可以安全地执行相同的操作,因为即使它效率低下,您也会受到影响。此外,斯坦