繁体   English   中英

std :: begin和R-values

[英]std::begin and R-values

最近我试图修复一个相当困难的const-correctness编译器错误。 它最初表现为Boost.Python内部的多段模板呕吐错误。

但这是无关紧要的:它都归结为以下事实:C ++ 11 std::beginstd::end迭代器函数不会重载以获取R值。

std::begin的定义是:

template< class C >
auto begin( C& c ) -> decltype(c.begin());

template< class C >
auto begin( const C& c ) -> decltype(c.begin());

因此,由于没有R值/通用引用重载,如果您传递一个R值,您将获得一个const迭代器。

那我为什么要关心? 好吧,如果您有某种“范围”容器类型,即像“视图”,“代理”或“切片”或某些容器类型,它们呈现另一个容器的子迭代器范围,通常非常方便使用R值语义并从临时切片/范围对象中获取非const迭代器。 但是使用std::begin ,你运气不好,因为std::begin将始终为R值返回一个const-iterator。 这是一个老问题,C ++ 03程序员经常在C ++ 11给我们R值的前一天感到沮丧 - 即临时问题总是绑定为const

那么,为什么不将std::begin定义为:

template <class C>
auto begin(C&& c) -> decltype(c.begin());

这样,如果c是常量,我们得到一个C::const_iterator和一个C::iterator

起初,我认为原因是为了安全。 如果你将一个临时文件传递给std::begin ,就像这样:

auto it = std::begin(std::string("temporary string")); // never do this

...你得到一个无效的迭代器。 但后来我意识到这个问题仍然存在于当前的实现中。 上面的代码只会返回一个无效的const -iterator,在解除引用时可能会出现段错误。

那么,为什么std::begin 没有被定义为采用R值(或更准确地说,是通用引用 )? 为什么有两个重载(一个用于const ,一个用于non-const )?

上面的代码只会返回一个无效的const-iterator

不完全的。 迭代器将一直有效,直到迭代器引用的临时表达式的完整表达式的结尾为止。所以类似于

std::copy_n( std::begin(std::string("Hallo")), 2,
             std::ostreambuf_iterator<char>(std::cout) );

仍然是有效的代码。 当然,在您的示例中, it在语句末尾无效。

修改临时或xvalue会有什么意义? 这可能是范围访问者的设计者在提出声明时所考虑的问题之一。 他们没有考虑.begin().end()返回的迭代器在其生命周期内有效的“代理”范围; 也许是因为在模板代码中,它们无法与正常范围区分开来 - 我们当然不希望修改临时非代理范围,因为这是毫无意义的,可能会导致混淆。

但是,您不需要首先使用std::begin ,而是可以使用using-declaration声明它们:

using std::begin;
using std::end;

并使用ADL。 这样就可以为Boost.Python(os)使用的类型声明命名空间范围的beginend重载,并规避std::begin的限制。 例如

iterator begin(boost_slice&& s) { return s.begin(); }
iterator end  (boost_slice&& s) { return s.end()  ; }

// […]

begin(some_slice) // Calls the global overload, returns non-const iterator

为什么有两个重载(一个用于const,一个用于非const)?

因为我们仍然希望支持rvalues对象(并且它们不能被T&形式的函数参数占用)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM