C++ 在迭代基本类型时使用const引用有什么缺点吗?
我发现自己最近越来越多地使用C++11,过去我一直在使用迭代器,现在我尽可能地使用:C++ 在迭代基本类型时使用const引用有什么缺点吗?,c++,templates,c++11,C++,Templates,C++11,我发现自己最近越来越多地使用C++11,过去我一直在使用迭代器,现在我尽可能地使用: std::vector<int> coll(10); std::generate(coll.begin(), coll.end(), []() { return rand(); } ); 但是如果集合元素类型是一个模板参数呢foo_func()可能会被重载,以便按常量引用传递复杂(=复制代价高昂)类型,按值传递简单类型: foo_func(const BigType& e) { ... }
std::vector<int> coll(10);
std::generate(coll.begin(), coll.end(), []() { return rand(); } );
但是如果集合元素类型是一个模板参数呢foo_func()
可能会被重载,以便按常量引用传递复杂(=复制代价高昂)类型,按值传递简单类型:
foo_func(const BigType& e) { ... };
foo_func(int e) { ... };
当我使用上面的C++03风格代码时,我没有考虑太多。我会以同样的方式进行迭代,因为取消引用常量迭代器会生成常量引用,所以一切都很好。但是使用基于C++11范围的for循环,我需要使用const reference循环变量来获得相同的行为:
for (const auto& e : coll) { foo_func(e); }
突然,我不再确定,如果auto
是一种简单的类型(比如实现引用的幕后指针),这是否会引入不必要的汇编指令
但是编译一个示例应用程序证实了简单类型没有开销,这似乎是在模板中使用基于范围的for循环的通用方法。如果不是这样的话,这将是一条道路
问题:标准中是否有任何保证
(我意识到这个问题实际上与基于范围的for循环无关。在使用const_迭代器时也会出现。)标准容器都从迭代器返回引用(但是,请注意,有些“容器不是真正的容器,例如,
std::vector
,它返回一个代理)。其他迭代器可能返回代理或值,尽管这不受严格支持
当然,该标准没有对性能做出任何保证。任何与性能相关的特性(超出复杂性保证)都被认为是实现的质量
这样说,你可能想考虑编译器是否为你做了选择:
for (auto&& e: coll) { f(e); }
这里的主要问题是f()
可能会收到一个非const
引用。如果必要,可以使用const
版本的coll6.5.4/1说明:
for ( for-range-declaration : braced-init-list ) statement
让range init等效于带括号的init列表
基于范围的for语句相当于
(下面将进一步解释所有这些\uuuu
gubbins的含义)
当然,与直接使用*\uu begin
而不是e
内部语句相比,该标准不保证该行const auto&e=*\uu begin
是否会引入性能开销。允许实现通过费力地将指针复制到某个堆栈槽中来实现引用然后在每次使用引用时将其读回,并且不需要进行优化
但是,如果\u begin
是一个容器迭代器(其运算符*
返回一个引用),那么合理的编译器没有理由会有开销,然后通过语句中的值传递e
。或…仅使用auto const&
;)@Xeo:如果迭代器生成的是值而不是[const
]对值的引用表示法T&&
推导值而不是对值的引用。哈?auto&&
将始终是一个引用,无论迭代器产生什么结果。如果它确实产生值,它将只是一个右值引用。(注:我可能误解了你的评论。)@Xeo:是的,e
的类型不是一个值类型,而是t&
对于某些类型的t
。当使用auto const&e
时,e
的类型是t const&
。要真正利用这种差异,可能需要使用f(std::forward(e))
这不太好看。@Daniel:自动常量&&
不是通用的引用,只会绑定到右值。谢谢Steve。那么你如何编写基于循环的通用范围呢?@Daniel:我想for(const auto&e:coll){foo_func(e);}
很好,但我还没有使用C++11来声称能够为自己编写一个风格指南来说明这一点。boost::call_traits::param_type
的用途是,一旦你传递了一个参数,那么在使用引用而不是小对象类型时会有潜在的开销,因为有一个调用约定限制在函数中,我相信编译器会将引用变量视为“纯”别名。如果*\uu begin
按值返回,我不会在不检查的情况下避免额外的临时变量。
for (auto&& e: coll) { f(e); }
for ( for-range-declaration : braced-init-list ) statement
{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}