简体   繁体   English

如何获取 C++20 std::ranges 范围内值的类型?

[英]How to get the type of the values in a C++20 std::ranges range?

Given a std::ranges::range in C++20, how can I determine the type of the values in that range?给定 C++20 中的std::ranges::range ,如何确定该范围内值的类型?

I want to write a function that makes a std::vector out of an arbitrary range.我想写一个 function 使std::vector超出任意范围。 I'd like this function to have a nice, explicit declaration.我希望这个 function 有一个很好的、明确的声明。 Something like:就像是:

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

The following seems to work, but the declaration is not explicit and the implementation is ugly (even ignoring that it doesn't allocate the right size up-front where possible).以下似乎有效,但声明不明确且实现很丑陋(甚至忽略它没有尽可能预先分配正确的大小)。

  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;
  }

Is there a canonical/better/shorter/nicer way to do this?有没有规范/更好/更短/更好的方法来做到这一点?

The type trait you are looking for is spelled std::ranges::range_value_t , not std::value_type_t .您正在寻找的类型特征拼写为std::ranges::range_value_t ,而不是std::value_type_t

Also, the whole function you are trying to write here is just a more limited version of std::ranges::to coming in C++23.此外,您在这里尝试编写的整个 function 只是 C++23 中std::ranges::to的一个更有限的版本。

Let's go through this in order:让我们 go 依次通过这个:

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

This is checking if Range is a range, but range isn't a Range , it's a const Range .这是检查Range是否是一个范围,但range不是Range ,它是一个const Range It's possible that R is a range but R const is not, so you're not actually constraining this function properly. R可能是一个range ,但R const不是,所以您实际上并没有正确地限制这个 function。

The correct constraint would be:正确的约束是:

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

But then that limits you to only const-iterable ranges (which is an unnecessary restriction) and then requires you to very carefully use Range const throughout the body (which is very easy to forget).但这会限制您只能使用 const-iterable 范围(这是不必要的限制),然后需要您非常小心地在整个主体中使用Range const (这很容易忘记)。

Both of which are why, with ranges, you'll want to use forwarding references:这两者都是为什么,对于范围,你会想要使用转发引用:

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

That'll constrain your function properly.这将正确限制您的 function 。


Next:下一个:

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

There are type traits for these things directly:这些东西直接有类型特征:

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

Or:或者:

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

But also since you want the value type, that's (as already pointed out):但也因为你想要值类型,那就是(正如已经指出的那样):

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

Note that the value type is not necessarily just the reference type with the qualifiers removed.请注意,值类型不一定只是删除了限定符的引用类型。


Lastly:最后:

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

It'll be more efficient or even more correct to forward the element into push_back (ie auto&& x and then FWD(x) , instead of auto const& x and x ).将元素转发到push_back会更有效甚至更正确(即auto&& x然后是FWD(x) ,而不是auto const& xx )。


Additionally you'll want to, at the very least:此外,您至少需要:

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

Since if we have the size readily available, it'll be nice to reduce the allocations to just the one.因为如果我们有随时可用的大小,那么将分配减少到只有一个会很好。


Lastly, in C++23, make_vector(r) can just be spelled std::ranges::to<std::vector>(r) .最后,在 C++23 中, make_vector(r)可以拼写为std::ranges::to<std::vector>(r) This does the allocation optimization I mentioned, but can additionally be more performant since vector 's construction internally can avoid the constant checking to see if additional allocation is necessary (which is what push_back has to do).这实现了我提到的分配优化,但还可以提高性能,因为vector的内部构造可以避免不断检查是否需要额外分配(这是push_back必须做的)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM