std :: begin和R值 [英] std::begin and R-values

查看:250
本文介绍了std :: begin和R值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我试图修复一个非常困难的const正确编译器错误。它最初表现为一个多段模板vomit错误深入Boost.Python。

Recently I was trying to fix a pretty difficult const-correctness compiler error. It initially manifested as a multi-paragraph template vomit error deep within Boost.Python.

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

But that's irrelevant: it all boiled down to the following fact: the C++11 std::begin and std::end iterator functions are not overloaded to take R-values.

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迭代器。

So since there is no R-value/Universal Reference overload, if you pass it an R-value you get a const iterator.

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

So why do I care? Well, if you ever have some kind of "range" container type, i.e. like a "view", "proxy" or a "slice" or some container type that presents a sub iterator range of another container, it is often very convenient to use R-value semantics and get non-const iterators from temporary slice/range objects. But with std::begin, you're out of luck because std::begin will always return a const-iterator for R-values. This is an old problem which C++03 programmers were often frustrated with back in the day before C++11 gave us R-values - i.e. the problem of temporaries always binding as const.

所以,为什么 std :: begin 定义为:

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

这样,如果 c 否则返回 C :: const_iterator C :: iterator

This way, if c is constant we get a C::const_iterator and a C::iterator otherwise.

起初,我想原因是为了安全。如果您将一个临时变量传递给 std :: begin ,如下所示:

At first, I thought the reason was for safety. If you passed a temporary to std::begin, like so:

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

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

...you'd get an invalid iterator. But then I realized this problem still exists with the current implementation. The above code would simply return an invalid const-iterator, which would probably segfault when dereferenced.

所以,为什么 std :: begin 定义为采用R值(或更准确地说, 通用参考 )?为什么有两个重载(一个用于 const 和一个用于非const )?

So, why is std::begin not defined to take an R-value (or more accurately, a Universal Reference)? Why have two overloads (one for const and one for non-const)?

推荐答案


上述代码只是返回一个无效的const迭代器

The above code would simply return an invalid const-iterator

不完全。迭代器将有效,直到完全表达式的结束,迭代器引用的临时表达式是在词法中创建的。

Not quite. The iterator will be valid until the end of the full-expression that the temporary the iterator refers to was lexically created in. So something like

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

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

is still valid code. Of course, in your example, it is invalidated at the end of the statement.

在修改临时值或xvalue时有什么意义?这可能是范围存取器的设计者在提出声明时所考虑的问题之一。他们没有考虑由 .begin() .end()返回的迭代器的代理有效期过期;也许是因为在模板代码中,它们不能与正常范围区分 - 我们当然不想修改临时非代理范围,因为这是无意义的,可能会导致混乱。

What point would there be in modifying a temporary or xvalue? That is probably one of the questions the designers of the range accessors had in mind when proposing the declarations. They didn't consider "proxy" ranges for which the iterators returned by .begin() and .end() are valid past its lifetime; Perhaps for the very reason that, in template code, they cannot be distinguished from normal ranges - and we certainly don't want to modify temporary non-proxy ranges, since that is pointless and might lead to confusion.

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

However, you don't need to use std::begin in the first place but could rather declare them with a using-declaration:

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

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

and use ADL. This way you declare a namespace-scope begin and end overload for the types that Boost.Python (o.s.) uses and circumvent the restrictions of std::begin. E.g.

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)?

Why have two overloads (one for const and one for non-const)?

因为我们仍然希望rvalues对象被支持(并且它们不能被 T& )。

Because we still want rvalues objects to be supported (and they cannot be taken by a function parameter of the form T&).

这篇关于std :: begin和R值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆