对于 STL 容器,我认为的共识std::begin
是“ ”。但是,根据,没有重载std::begin(C&& c)
,因此如果我std::begin
使用右值引用调用,则会const&
调用 -version,从而得到一个C::const_iterator
,这似乎违反直觉(为什么我不能修改右值容器?)并且不同意C.begin() &&
(返回一个C::iterator
):
#include <unordered_map>
#include <type_traits>
using MyMap = std::unordered_map<int, int>;
using Iter = typename MyMap::iterator;
using ConstIter = typename MyMap::const_iterator;
static_assert(std::is_same_v<decltype(MyMap().begin()), Iter>);
static_assert(std::is_same_v<decltype(std::begin(MyMap())), Iter>); // <-- fails
static_assert(std::is_same_v<decltype(MyMap().begin()), ConstIter>); // <-- fails
static_assert(std::is_same_v<decltype(std::begin(MyMap())), ConstIter>);
玩)
所以我想知道,这是标准/ gcc 和 clang 中的错误/疏忽,还是我做错了什么?
编辑:我目前正在使用这个解决方法
template<class T> concept has_begin = requires (T t) { t.begin(); };
template<class T>
decltype(auto) my_begin(T&& x) {
if constexpr(has_begin<std::remove_reference_t<T>>)
return std::forward<T>(x).begin();
else return std::begin(x);
}
但它似乎是标准中一个错误的解决方法,因此才有这个问题。
PS:是的,我知道这是一个不常见的用例。我建议使用以下代码作为(人为的)示例:
template<class T>
decltype(auto) do_things(T&& in) {
const auto _end = in.end();
auto it = std::begin(std::forward<T>(in));
while(it != _end) {
it->second += it->first; // fails, even when passing non-const rvalues as 'in'
++it;
}
return std::forward<T>(in);
}
现在,有人可能会说std::begin(std::forward<T>(in))
应该只是std::begin(in)
,但我有一些类begin() &&
与他们的有很大不同begin() &
,我可能会把它们当作in
。
5
最佳答案
1
在右值容器上调用任何一个都可能是错误的。该容器将在完整表达式的末尾不复存在,并且您无法引用它来获取结束迭代器。std::begin(MyMap()), std::end(MyMap())
不是有效范围,这些迭代器引用不同的对象。
如果您有一个右值引用,那么您可以使用它来提供范围的两端,但是这样做时您就有一个左值。
auto && myMap = MyMap();
std::begin(myMap), std::end(myMap); // <- uses std::begin(MyMap&)
11
-
但是有一个正确的用法:或者如果重载的
std::begin(std::move(myMap))
话就会是这样。std::begin
&&
– -
@bolov 是的,但这意味着
std::end(std::move(myMap))
这并没有错,只是看起来很可疑。
– -
“可能”并不意味着“必然”。事实上,我正在编写一个高度模板化的库,并且(出于复杂的原因)我调用了很多,当…时
std::begin(std::forward<T>(x))
,它会中断。x
T&&
– -
@igel 为什么对你不起作用
std::begin(x)
?也许你想要的是forward_iterator<T>(std::begin(x))
,如果是右值,则将其forward_iterator
包装在其中,如果是左值,则不加改变地传递。std::move_iterator
T
T
– -
1如果您的
traversal
s 始终是您控制的类,那么您可以使用std::forward<T>(x).begin()
.existsstd::begin
因为数组没有成员函数。
–
|
std::move_iterator
或类似的类型。–
std::move_iterator
。这似乎正是我想要的!–
std::ranges::begin
解决了一些与std::begin
左值/右值和 constness 有关的问题,但我不知道具体是什么。这可能是您所需要的。–
do_things
有点可疑。您正在转发两次,这意味着它可能已被移出。您应该在std::begin(in)
那里,in
始终是一个左值。–
begin() &&
调用后变得无法使用,但这种情况可能发生也可能不发生……–
|