我试图为我的c++20的扩展实现一个通用的归约操作,ranges
该操作会将 any 的元素收集range
到给定的容器中。为了实现这一点,我首先创建了一个用于提取template template
参数的虚拟类型,并提供了operator|
用于将 arange
与它结合起来:
template <template <typename> typename T>
struct to_fn { };
template <template <typename> typename T>
inline constexpr detail::functors::to_fn<T> to;
template <template <typename> typename T>
auto operator|(std::ranges::range auto&& rng, detail::functors::to_fn<T>) {
return T(std::ranges::begin(rng), std::ranges::end(rng));
}
测试如下:
int main() {
using namespace std::ranges;
std::vector<int> vec = {1, 2, 3, 4, 5};
auto set = vec | to<std::set>;
static_assert(std::same_as<decltype(set), std::set<int>>);
assert(equal(vec, set));
}
代码完成执行没有问题。
但是,当与以下代码一起使用时,代码无法编译std::ranges::istream_view
:
int main() {
using namespace std::ranges;
std::ifstream input_file("input.txt");
auto vec = istream_view<int>(input_file) | to<std::vector>;
}
这无法通过一堵错误墙进行编译,在我看来,其中最重要的一个是:
note: deduced conflicting types for parameter '_InputIterator' ('std::ranges::basic_istream_view<int, char, std::char_traits<char> >::_Iterator' and 'std::default_sentinel_t') 122 | return T(std::ranges::begin(rng), std::ranges::end(rng)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这对我来说很有意义。容器要求用于通过构造函数构造它们的迭代器具有相同的类型。
但这很好 - 这std::ranges::views::common_view
就是创建的目的。所以我尝试修改operator|
为:
template <template <typename> typename T>
auto operator|(std::ranges::range auto&& rng, detail::functors::to_fn<T>) {
auto common = rng | std::ranges::views::common;
return T(std::ranges::begin(common), std::ranges::end(common));
}
再次,它未能以较小的错误墙编译,其中我认为这是最相关的:
note: the expression 'is_constructible_v<_Tp, _Args ...> [with _Tp = std::ranges::basic_istream_view<int, char, std::char_traits<char> >::_Iterator<int, char, std::char_traits<char> >; _Args = {std::ranges::basic_istream_view<int, char, std::char_traits<char> >::_Iterator<int, char, std::char_traits<char> >&}]' evaluated to 'false' 139 | = destructible<_Tp> && is_constructible_v<_Tp, _Args...>; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我不太明白这个错误表示什么,但我想这意味着它istream_view
不能被复制构造。对我来说有点道理。
但我真的希望我能拥有这个通用的to
“函子”。当我们推断我们正在处理输入范围1istream_view
时,我认为可以使用基于范围的for
循环并将元素添加到所选容器中。
所以我尝试了这个:
template <template <typename> typename T>
auto operator|(std::ranges::range auto&& rng, detail::functors::to_fn<T>) {
using namespace std::ranges;
using range_t = decltype(rng);
const bool input_range = std::is_same_v<
iterator_t<range_t>::iterator_category,
std::input_iterator_tag>;
if constexpr(input_range) {
auto container = T<range_value_t<range_t>>();
for (auto&& element : rng) {
container.generic_add(element); // ???
}
return container;
} else {
auto common = rng | views::common;
return T(begin(common), end(common));
}
}
然后告诉我,除其他外:
error: 'iterator_category' is not a member of 'std::ranges::iterator_t<std::ranges::basic_istream_view<int, char, std::char_traits<char> >&&>' 125 | iterator_t<range_t>::iterator_category, | ^~~~~~~~~~~~~~~~~
这不是唯一的问题。通常将元素添加到任何容器中也存在问题。range
据我所知,采用 a 的构造函数是向容器添加元素的唯一通用方法和好方法。
我觉得必须有一种正确且更简单的方法来做我想做的事情。to
如果也适用于非模板,则奖励积分,即,我不仅可以这样做to<std::vector>
,而且to<std::string>
. 在第一种情况下,它将推断元素并创建所需的实例化std::vector
,但在第二种情况下,它将获取所有元素并std::string
用这些元素初始化 an。我怎样才能使这项工作?
1这假设实际问题在于我们正在使用输入范围这一事实。我不确定是不是这样。如果有人能指出我推理中可能存在的错误,我会很高兴。