为什么范围不为非连续范围提供类型擦除视图?
Why doesn't ranges provide a type erased view for non-contiguous ranges?
我想要一个函数,它可以接受任何 range/view 固定值类型。
int main()
{
std::array<std::pair<int, int>, 2> a{...};
std::array<std::pair<int, int>, 3> b{...};
generic_fun(a);
generic_fun(b);
};
当然可以
template <std::ranges::range R>
requires std::same_as<std::ranges::range_value_t<R>,std::pair<int,int>>
auto generic_fun(R range)
{
for(const auto& element : range)
return element.first;
}
但是 visual studio ide 不知道 element
的类型。
我期待范围库有一些类似
的类型
template <typename T>
struct view
{
template <std::ranges::range R>
requires std::same_as<std::ranges::range_value_t<R>, T>
view(R);
T* begin() const;
T* end() const;
};
这会给我 ide 支持
auto generic_fun(view<std::pair<int,int>> a)
{
for (const auto& b : a)
return b.first;
}
为什么范围库中不存在这样的类型?定义一个抽象出除 ranges/iterators 值类型以外的所有类型的类型在技术上是不可行的吗?或者没有人关心这样做,因为唯一的原因是 ide 支持?
虽然拥有一个包装给定迭代器概念的类型(在给定值类型上模板化)是很自然的,但 std 没有定义任何。 (如果互联网上有关于包装概念的类型的内容,请指点一下好吗?)
range-v3 在名称 any_view<Ref, Cat>
下有这个,其中 Ref
是范围的 reference
(不是 value_type
),Cat
是迭代类别(默认为 input
)。那会让你写一个像这样的函数:
int sum(any_view<int const&> v) { // <== not a function template
int s = 0;
for (int i : v) {
s += i;
}
return s;
}
std::list<int> l = {1, 2, 3};
assert(sum(l) == 3);
问题是,这个射程适配器 非常 昂贵。想想迭代的工作方式。我们有一个像这样的循环:
for (; it != end; ++it) {
use(*it);
}
即使对于输入迭代器,这也意味着您必须键入擦除:
operator!=
operator++
operator*
那是 三个 间接调用每个元素(virtual
函数调用,或通过函数指针,具体取决于执行)。这是一个很大的开销,而且它很少是您真正想要使用的东西。因此,就添加到 C++20 中的范围适配器而言,它的优先级非常低,甚至不在我们 plan 中用于将来添加范围适配器。
现在,对于连续的范围,情况有点不同,因为您始终可以只存储 (T*, size_t)
,而原始范围是什么并不重要。它仍然是类型擦除,但它是免费的——没有额外的开销。所以 span<T>
在这方面很棒。
Or does no-one care about doing this because the only reason is ide support?
有些情况下类型擦除实际上很重要。也许您正在存储一堆不同类型的范围。也许您在 ABI 边界上隐藏了它。但是 IDE 支持似乎是使用类型擦除的一个非常微弱的动机——尤其是在这样的上下文中,这样做会产生相当大的性能开销。
[...] but then the visual studio ide doesn't know the type of element
.
您也可以通过不使用 auto
.
来解决这个问题
我想要一个函数,它可以接受任何 range/view 固定值类型。
int main()
{
std::array<std::pair<int, int>, 2> a{...};
std::array<std::pair<int, int>, 3> b{...};
generic_fun(a);
generic_fun(b);
};
当然可以
template <std::ranges::range R>
requires std::same_as<std::ranges::range_value_t<R>,std::pair<int,int>>
auto generic_fun(R range)
{
for(const auto& element : range)
return element.first;
}
但是 visual studio ide 不知道 element
的类型。
我期待范围库有一些类似
的类型template <typename T>
struct view
{
template <std::ranges::range R>
requires std::same_as<std::ranges::range_value_t<R>, T>
view(R);
T* begin() const;
T* end() const;
};
这会给我 ide 支持
auto generic_fun(view<std::pair<int,int>> a)
{
for (const auto& b : a)
return b.first;
}
为什么范围库中不存在这样的类型?定义一个抽象出除 ranges/iterators 值类型以外的所有类型的类型在技术上是不可行的吗?或者没有人关心这样做,因为唯一的原因是 ide 支持?
虽然拥有一个包装给定迭代器概念的类型(在给定值类型上模板化)是很自然的,但 std 没有定义任何。 (如果互联网上有关于包装概念的类型的内容,请指点一下好吗?)
range-v3 在名称 any_view<Ref, Cat>
下有这个,其中 Ref
是范围的 reference
(不是 value_type
),Cat
是迭代类别(默认为 input
)。那会让你写一个像这样的函数:
int sum(any_view<int const&> v) { // <== not a function template
int s = 0;
for (int i : v) {
s += i;
}
return s;
}
std::list<int> l = {1, 2, 3};
assert(sum(l) == 3);
问题是,这个射程适配器 非常 昂贵。想想迭代的工作方式。我们有一个像这样的循环:
for (; it != end; ++it) {
use(*it);
}
即使对于输入迭代器,这也意味着您必须键入擦除:
operator!=
operator++
operator*
那是 三个 间接调用每个元素(virtual
函数调用,或通过函数指针,具体取决于执行)。这是一个很大的开销,而且它很少是您真正想要使用的东西。因此,就添加到 C++20 中的范围适配器而言,它的优先级非常低,甚至不在我们 plan 中用于将来添加范围适配器。
现在,对于连续的范围,情况有点不同,因为您始终可以只存储 (T*, size_t)
,而原始范围是什么并不重要。它仍然是类型擦除,但它是免费的——没有额外的开销。所以 span<T>
在这方面很棒。
Or does no-one care about doing this because the only reason is ide support?
有些情况下类型擦除实际上很重要。也许您正在存储一堆不同类型的范围。也许您在 ABI 边界上隐藏了它。但是 IDE 支持似乎是使用类型擦除的一个非常微弱的动机——尤其是在这样的上下文中,这样做会产生相当大的性能开销。
[...] but then the visual studio ide doesn't know the type of
element
.
您也可以通过不使用 auto
.