我试图为我的扩展实现一个通用的缩减操作c++20's ranges
这将收集任何元素range
到给定的容器中。为了实现这一点,我首先创建了一个虚拟类型来提取template template
参数并提供operator|
用于梳理一个range
用它:
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>;
}
This fails编译时出现了大量错误,我认为其中最重要的是:
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|
to:
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));
}
再说一次,failed编译时出现的错误较少,我认为其中最相关的是:
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
不能被复制构造。对我来说有点道理。
But I really wish I could have this generic to
"functor". I figured that it's okay to loop over istream_view
with range-based for
and add elements to the chosen container, when we deduce that we are dealing with an input range1.
所以我尝试了这个:
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
据我所知,这是唯一通用的方法good向容器添加元素的方法。
我觉得必须有一种正确且更简单的方法来完成我想做的事情。奖励积分如果to
也适用于非模板,即我不仅可以这样做to<std::vector>
但是也to<std::string>
。在第一种情况下,它将推断元素并创建所需的实例化std::vector
,但在第二种情况下,它将获取所有元素并初始化一个std::string
和那些。我怎样才能做到这一点?
1 This assumes that the actual problem lies in the fact that we are using input range. I am not sure whether that's the case. I would love if someone could point out the possible error in my reasoning.