std::ranges::to 是否允许转换为 std::map?
Does std::ranges::to allow converting to a std::map?
在 std::ranges::to
论文 wg21.link/p1206 中,概述部分具有以下内容
//Supports converting associative container to sequence containers
auto f = ranges::to<vector>(m);
但是我找不到在论文的其余部分描述转换为 std::map
的细节的地方。我尝试了 range-v3 和 Sy Brand 在 https://github.com/TartanLlama/ranges 中对 ranges::to
的实现,但它们都无法编译将范围转换为 std::map
的代码。那么这只是这些库中缺少的,还是正在转换为 std::map
并非真正打算允许的?
So is this just missing from those libraries or is converting to a
std::map
not really intended to be allowed?
std::map 到 std::vector:
根据[range.utility.conv.to]的描述,这个
map<int, double> m;
auto f = ranges::to<vector>(m);
将调用以下重载
template<template<class...> class C, input_range R, class... Args>
constexpr auto to(R&& r, Args&&... args);
Let DEDUCE_EXPR
be defined as follows:
C(declval<R>(), declval<Args>()...)
if that is a valid expression,
- otherwise,
C(from_range, declval<R>(), declval<Args>()...)
if that is a valid expression,
- otherwise,
C(declval<input-iterator>(), declval<input-iterator>(), declval<Args>()...)
if that is a valid expression,
- otherwise, the program is ill-formed.
Returns: to<decltype(DEDUCE_EXPR)>(std::forward<R>(r), std::forward<Args>(args)...)
.
其中C
为vector
,R
为map<int, double>&
,Args...
为空参数包。请注意,C++23 为 vector
引入了以下 range version constructor
template<container-compatible-range<T> R>
constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());
Effects: Constructs a vector object with the elements of the range rg
,
using the specified allocator.
及以下 CTAD
template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
vector(from_range_t, R&&, Allocator = Allocator())
-> vector<ranges::range_value_t<R>, Allocator>;
所以C(from_range, declval<R>())
是一个有效的表达式,DEDUCE_EXPR
的类型将是vector<pair<const int, double>>
,这将进一步调用
to<vector<pair<const int, double>>>(m);
这将通过范围版本构造函数构造一个vector
,其中value_type
为pair<const int, double>
。
所以C++23中的ranges::to<vector>(m)
基本上等同于
map<int, double> m;
vector f(m.begin(), m.end());
之所以range-v3 fails是因为它的内部实现检测到vector<pair<const int, double>>
是可预留的,所以会先默认构造vector
然后调用v.reserve()
到pre-allocate 内存,然后通过调用 v.assign()
复制 map
,但由于 pair<const int, double>
不可复制分配,因此编译失败。
我怀疑这是 range-v3 的实现错误,因为如果 vector
的 reserve()
函数不存在它会编译,并且这个优化的重载似乎并不限制 value_type
必须是 copy-assignable.
std::vector 到 std::map:
以及以下
auto g = ranges::to<map>(f);
因为 std::map
在 C++23
中也有 following constructors 和相应的 CATD
template<container-compatible-range<value_type> R>
map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
所以按照同样的游戏规则,我们会得到一个map<int, double>
.
Does std::ranges::to
allow converting to a std::map
?
是的。
I tried range-v3 and Sy Brand's implementation of ranges::to
in https://github.com/TartanLlama/ranges and neither of them compiles code converting a range to a std::map
我还没有尝试过 Sy 的实现,看起来 range-v3 的实现很奇怪:
#include <map>
#include <vector>
#include <range/v3/range/conversion.hpp>
int main() {
std::vector<std::pair<int, int>> v = {{1, 2}, {3, 4}};
// this works (explicit)
// m1 is a std::map<int, int>
auto m1 = ranges::to<std::map<int, int>>(v);
// this works (deduced with a pipe)
// m2 is a std::map<int, int>
auto m2 = v | ranges::to<std::map>();
// but this does not (deduced, direct call)
auto m3 = ranges::to<std::map>(v);
}
问题是 range-v3 中的 class 模板直接调用版本出于某种原因专门尝试实例化 C<range_value_t<R>>
(在本例中为 std::map<std::pair<int, int>>
,显然是错误的) 即使这里已经有一个元函数可以做正确的事情并且会推断 std::map<int, int>
(由管道版本使用)。
标准库中的 ranges::to
指定这两个做同样(正确)的事情,所以这将在 C++23 中工作。这只是 range-v3 中的一个简单错误修复。
在 std::ranges::to
论文 wg21.link/p1206 中,概述部分具有以下内容
//Supports converting associative container to sequence containers
auto f = ranges::to<vector>(m);
但是我找不到在论文的其余部分描述转换为 std::map
的细节的地方。我尝试了 range-v3 和 Sy Brand 在 https://github.com/TartanLlama/ranges 中对 ranges::to
的实现,但它们都无法编译将范围转换为 std::map
的代码。那么这只是这些库中缺少的,还是正在转换为 std::map
并非真正打算允许的?
So is this just missing from those libraries or is converting to a
std::map
not really intended to be allowed?
std::map 到 std::vector:
根据[range.utility.conv.to]的描述,这个
map<int, double> m;
auto f = ranges::to<vector>(m);
将调用以下重载
template<template<class...> class C, input_range R, class... Args> constexpr auto to(R&& r, Args&&... args);
Let
DEDUCE_EXPR
be defined as follows:
C(declval<R>(), declval<Args>()...)
if that is a valid expression,- otherwise,
C(from_range, declval<R>(), declval<Args>()...)
if that is a valid expression,- otherwise,
C(declval<input-iterator>(), declval<input-iterator>(), declval<Args>()...)
if that is a valid expression,- otherwise, the program is ill-formed.
Returns:
to<decltype(DEDUCE_EXPR)>(std::forward<R>(r), std::forward<Args>(args)...)
.
其中C
为vector
,R
为map<int, double>&
,Args...
为空参数包。请注意,C++23 为 vector
template<container-compatible-range<T> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());
Effects: Constructs a vector object with the elements of the range
rg
, using the specified allocator.
及以下 CTAD
template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
vector(from_range_t, R&&, Allocator = Allocator())
-> vector<ranges::range_value_t<R>, Allocator>;
所以C(from_range, declval<R>())
是一个有效的表达式,DEDUCE_EXPR
的类型将是vector<pair<const int, double>>
,这将进一步调用
to<vector<pair<const int, double>>>(m);
这将通过范围版本构造函数构造一个vector
,其中value_type
为pair<const int, double>
。
所以C++23中的ranges::to<vector>(m)
基本上等同于
map<int, double> m;
vector f(m.begin(), m.end());
之所以range-v3 fails是因为它的内部实现检测到vector<pair<const int, double>>
是可预留的,所以会先默认构造vector
然后调用v.reserve()
到pre-allocate 内存,然后通过调用 v.assign()
复制 map
,但由于 pair<const int, double>
不可复制分配,因此编译失败。
我怀疑这是 range-v3 的实现错误,因为如果 vector
的 reserve()
函数不存在它会编译,并且这个优化的重载似乎并不限制 value_type
必须是 copy-assignable.
std::vector 到 std::map:
以及以下
auto g = ranges::to<map>(f);
因为 std::map
在 C++23
template<container-compatible-range<value_type> R>
map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
所以按照同样的游戏规则,我们会得到一个map<int, double>
.
Does
std::ranges::to
allow converting to astd::map
?
是的。
I tried range-v3 and Sy Brand's implementation of
ranges::to
in https://github.com/TartanLlama/ranges and neither of them compiles code converting a range to astd::map
我还没有尝试过 Sy 的实现,看起来 range-v3 的实现很奇怪:
#include <map>
#include <vector>
#include <range/v3/range/conversion.hpp>
int main() {
std::vector<std::pair<int, int>> v = {{1, 2}, {3, 4}};
// this works (explicit)
// m1 is a std::map<int, int>
auto m1 = ranges::to<std::map<int, int>>(v);
// this works (deduced with a pipe)
// m2 is a std::map<int, int>
auto m2 = v | ranges::to<std::map>();
// but this does not (deduced, direct call)
auto m3 = ranges::to<std::map>(v);
}
问题是 range-v3 中的 class 模板直接调用版本出于某种原因专门尝试实例化 C<range_value_t<R>>
(在本例中为 std::map<std::pair<int, int>>
,显然是错误的) 即使这里已经有一个元函数可以做正确的事情并且会推断 std::map<int, int>
(由管道版本使用)。
标准库中的 ranges::to
指定这两个做同样(正确)的事情,所以这将在 C++23 中工作。这只是 range-v3 中的一个简单错误修复。