如何在c++ 20 std::ranges范围中获得值的类型?



给定c++ 20中的std::ranges::range,我如何确定该范围内值的类型?

我想写一个函数,使std::vector的任意范围。我希望这个函数有一个漂亮的显式声明。比如:

template<std::ranges::range Range>
std::vector<std::value_type_t<Range>> make_vector(Range const&);

下面的代码似乎可以工作,但是声明不显式,实现也很难看(甚至忽略了它没有在可能的情况下预先分配正确的大小)。

template<std::ranges::range Range>
auto make_vector(Range const& range)
{
using IteratorType = decltype(std::ranges::begin(std::declval<Range&>()));
using DerefType    = decltype(*std::declval<IteratorType>());
using T            = std::remove_cvref_t<DerefType>;
std::vector<T> retval;
for (auto const& x: range) {
retval.push_back(x);
}
return retval;
}

是否有规范/更好/更短/更好的方法来做这件事?

您正在寻找的类型特征拼写为std::ranges::range_value_t,而不是std::value_type_t

同样,你在这里试图写的整个函数只是c++ 23中std::ranges::to的一个更有限的版本。

让我们按顺序看一遍:

template<std::ranges::range Range>
auto make_vector(Range const& range)

这是检查Range是否是一个范围,但range不是Range,而是const Range。有可能Rrange,但R const不是,所以你实际上并没有正确地约束这个函数。

正确的约束应该是:

template<typename Range>
requires std::ranges::range<Range const>
auto make_vector(Range const& range)

但是这会限制您只能使用const-iterable范围(这是一个不必要的限制),然后要求您在整个主体中非常小心地使用Range const(这很容易忘记)。

这两个都是为什么,对于范围,你会想要使用转发引用:

template<std::ranges::range Range>
auto make_vector(Range&& range)

这将适当地约束你的功能。

下:

using IteratorType = decltype(std::ranges::begin(std::declval<Range&>()));
using DerefType    = decltype(*std::declval<IteratorType>());
using T            = std::remove_cvref_t<DerefType>;

这些东西有直接的类型特征:

using IteratorType = std::ranges::iterator_t<Range>;
using DerefType = std::iter_reference_t<IteratorType>;

或:

using DerefType = std::ranges::range_reference_t<Range>;

但也因为你想要的值类型,这是(已经指出):

using T = std::ranges::range_value_t<Range>;

注意,值类型不一定只是去掉限定符的引用类型。


最后:

std::vector<T> retval;
for (auto const& x: range) {
retval.push_back(x);
}
return retval;

将元素转发到push_back(即auto&& x然后FWD(x),而不是auto const& xx)将更有效甚至更正确。


另外,您至少需要:

if constexpr (std::ranges::sized_range<Range>) {
retval.reserve(std::ranges::size(range));
}

因为如果我们有可用的大小,那么最好将分配减少到一个。


最后,在c++ 23中,make_vector(r)可以直接拼写为std::ranges::to<std::vector>(r)。这做了我提到的分配优化,但可以额外提高性能,因为vector的内部构造可以避免不断检查是否需要额外的分配(这是push_back必须做的)。

最新更新