C++23引入了非常强大的ranges::to,用于从范围内构造对象(通常是容器),定义如下([range.utility.conv.to]):
template<class C, input_range R, class... Args> requires (!view<C>)
constexpr C to(R&& r, Args&&... args);请注意,它只将模板参数C限制为不属于view,也就是说,C甚至可能不是range。
但是,它的实现使用range_value_t<C>获取C的元素类型,这使得C至少成为一个range,因为模板参数R必须对range建模这一range_value_t约束。
那么,为什么ranges::to对模板参数C的约束如此松散呢?
我注意到论文的R3版本以前将C约束为input_range,这显然是合理的,因为input_range保证range_value_t格式良好,但是在R4中,这个约束被删除了。我没有找到任何关于这一变化的评论。
因此,删除C必须是input_range的约束需要考虑哪些事项?
是否有一个实际的例子来说明这种约束放松的好处?
发布于 2022-09-19 16:12:46
这是一个与我们需要解决的措辞问题,我将在今天晚些时候开始一个问题。我是LWG 3785。
那么,删除
C必须是input_range的约束需要考虑什么呢?
ranges::to的目标是收集一个范围.某物。但它不必是一个实际的范围。只是消耗了所有元素的东西。当然,最常见的用法是实际的容器类型,而最常见的实际容器类型将是std::vector。
还有其他有趣的用例,但实际上没有太多的理由拒绝。
假设我们有一个范围的std::expected<int, std::exception_ptr>,称为results。也许我们做了一些计算,也许有些计算失败了。我可以把它收集到一个std::vector<std::expected<int, std::exception_ptr>>中,这可能很有用。但是还有另一种选择:我可以把它收集到一个std::expected<std::vector<int>, std::exception_ptr>中。也就是说,如果所有的计算都成功了,我将得到所有结果的值类型。但是,如果其中任何一个失败了,我将得到第一个错误。这是一件非常有用的事情,这在概念上非常符合ranges::to对其输入所做的事情--所以这可以支持:
auto processed = results | ranges::to<std::expected>();
if (not processed) {
std::rethrow_exception(processed.error());
}
std::vector<int> values = std::move(processed).value();
// go do more stuff这对于支持是非常有用的--特别是因为不支持它并不需要付出任何代价。我们只是不能过早地拒绝它。
https://stackoverflow.com/questions/73768399
复制相似问题