Sil*_*ler 7 c++ iterator c++11
最近我试图修复一个相当困难的const-correctness编译器错误.它最初表现为Boost.Python内部的多段模板呕吐错误.
但这是无关紧要的:这一切都归结为以下事实:C++ 11 std::begin和std::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());
Run Code Online (Sandbox Code Playgroud)
因此,由于没有R值/通用引用重载,如果您传递一个R值,您将获得一个const迭代器.
那我为什么要关心?好吧,如果您有某种"范围"容器类型,即像"视图","代理"或"切片"或某些容器类型,它们呈现另一个容器的子迭代器范围,通常非常方便使用R值语义并从临时切片/范围对象中获取非const迭代器.但是std::begin,你运气不好因为std::begin总会返回一个R值的常量迭代器.这是一个老问题,C++ 03程序员经常在C++ 11给我们R值的前一天感到沮丧 - 即临时问题始终具有约束力const.
那么,为什么没有std::begin定义为:
template <class C>
auto begin(C&& c) -> decltype(c.begin());
Run Code Online (Sandbox Code Playgroud)
这样,如果c是常数,我们得到a C::const_iterator和a C::iterator否则.
起初,我认为原因是为了安全.如果你传递了一个临时的std::begin,就像这样:
auto it = std::begin(std::string("temporary string")); // never do this
Run Code Online (Sandbox Code Playgroud)
...你得到一个无效的迭代器.但后来我意识到这个问题仍然存在于当前的实现中.上面的代码只会返回一个无效的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) );
Run Code Online (Sandbox Code Playgroud)
仍然是有效的代码.当然,在您的示例中,it在语句结束时无效.
修改临时或xvalue会有什么意义?这可能是范围访问者的设计者在提出声明时所考虑的问题之一.他们没有考虑"代理"范围,这由返回的迭代.begin()和.end()是过去它的寿命有效; 也许是因为在模板代码中,它们无法与正常范围区分开来 - 我们当然不希望修改临时非代理范围,因为这是毫无意义的,可能会导致混淆.
但是,您不需要首先使用它std::begin,而是可以使用using声明声明它们:
using std::begin;
using std::end;
Run Code Online (Sandbox Code Playgroud)
并使用ADL.这样就可以为Boost.Python(os)使用的类型声明一个名称空间范围begin和end重载,并规避其限制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
Run Code Online (Sandbox Code Playgroud)
为什么有两个重载(一个用于const,一个用于非const)?
因为我们仍然希望支持rvalues对象(并且它们不能被表单的函数参数占用T&).