在下面的代码中,auto [i, e]
和auto&& [i, ee]
都绑定了std::pair<int, T&>
而不是std::pair<int, T>
。有人能解释一下,如果没有实证检验,怎么会知道这一点吗?我想是range-v3实现。是否存在要使用auto&&
而不是auto
的情况?
auto container = std::array<int,3>{ 1,2,3 };
for (std::pair<int, int> p : ranges::views::enumerate(container))
p.second = 0; //Bad, of course
print(container);
for (auto [i, e] : ranges::views::enumerate(container))
e = 0; //Okay???
print(container);
for (auto&& [i, ee] : ranges::views::enumerate(container))
ee = 42; //Okay???
print(container);
> [1,2,3]
> [0,0,0]
> [42,42,42]
https://godbolt.org/z/b7vrsxqK4
编辑:使用static_assert(std::is_reference_v<decltype(variable_name)>);
我很困惑,因为通常在ranged for循环中,当在迭代器上调用*
运算符时,它会返回T&
,所以必须添加ref限定符才能获得引用而不是副本。
for(auto& el : container)
---------------
auto& el = *it; // reference
for(auto el : container)
---------------
auto el = *it; // copy
然而,当*
返回std::pair<int, T&>
时,您不能再使用auto&
,但现在auto
将正常工作,因为它是被复制分配的对的引用,而不是对T&
内部的引用。
将其与结构化绑定相结合,实际发生的事情非常令人困惑,因为下面的el
实际上是一个参考:
for (auto [i, el] : views::enumerate(container))
el = 3; // OKAY!
要在将迭代器取消引用到给定范围时找到类型,可以使用
template<typename... Args> void whatis();
int main()
{
auto container = std::array<int, 3>{ 1, 2, 3 };
auto it = ranges::views::enumerate(container).begin();
using traits = std::iterator_traits<decltype(it)>;
whatis<traits::reference>();
}
这将告诉你for(auto p : enumerate(container))
中的推断类型将是ranges::common_pair<unsigned int,int &>
类似地,你可以发现ranges::views::enumerate(container)
的类型是ranges::detail::index_view<unsigned int,int>,struct ranges::ref_view<class std::array<int,3>>>
。
从那里你可以开始研究源代码。CCD_ 20传递到CCD_ 21,CCD_
/// If it's a view already, pass it though.
template<typename T>
static constexpr auto from_range_(T&& t, std::true_type, detail::ignore_t,
detail::ignore_t)
{
return static_cast<T&&>(t);
}
/// If it is container-like, turn it into a view, being careful
/// to preserve the Sized-ness of the range.
template<typename T>
static constexpr auto from_range_(T&& t, std::false_type, std::true_type,
detail::ignore_t)
{
return ranges::views::ref(t);
}
/// Not a view and not an lvalue? If it's a borrowed_range, then
/// return a subrange holding the range's begin/end.
template<typename T>
static constexpr auto from_range_(T&& t, std::false_type, std::false_type,
std::true_type)
{
return make_subrange(static_cast<T&&>(t));
}
public:
template(typename T)(
/// pre
requires range<T&> AND viewable_range<T>)
constexpr auto operator()(T&& t) const
{
return all_fn::from_range_(static_cast<T&&>(t),
meta::bool_<view_<uncvref_t<T>>>{},
std::is_lvalue_reference<T>{},
meta::bool_<borrowed_range<T>>{});
}
然后,您可以查看ref_view
,并看到它确实按照名称所示进行操作(参考底层容器(。