gpt4 book ai didi

c++ - 使用基于概念的递归函数模板在扣除 'auto [...] ' 之前使用 'auto'

转载 作者:行者123 更新时间:2023-12-01 12:43:59 28 4
gpt4 key购买 nike

我想创建一个 deep_flatten会产生 range 的函数模板深的元素join编。例如,如果我们只考虑嵌套 std::vector s,我可以有:

template <typename T>
struct is_vector : public std::false_type { };

template <typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type { };

template <typename T>
auto deepFlatten(const std::vector<std::vector<T>>& vec) {
using namespace std::ranges;
if constexpr (is_vector<T>::value) {
auto range = vec | views::join;
return deepFlatten(std::vector(range.begin(), range.end()));
} else {
auto range = vec | views::join;
return std::vector(range.begin(), range.end());
}
}
这使我能够做到:
std::vector<std::vector<std::vector<int>>> nested_vectors = {
{{1, 2, 3}, {4, 5}, {6}},
{{7}, {8, 9}, {10, 11, 12}},
{{13}}
};

std::ranges::copy(
deep_flatten(nested_vectors),
std::ostream_iterator<int>(std::cout, " ")
);
正如预期的那样,它将以下文本打印到控制台中:
1 2 3 4 5 6 7 8 9 10 11 12 13

但是 ,我不太喜欢这个解决方案。它不仅效率低下(创建了许多临时 vector ),而且还仅适用于 std::vector s。我想我可以使用更多 魔法与使用 std::ranges::range概念:
namespace rng {
template <std::ranges::range Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;

if constexpr (range<Rng>) {
return deep_flatten(rng | views::join);
} else {
return rng | views::join;
}
}
}
这在我看来非常简单——我们有一个 std::ranges::range我们检查它的值类型。根据它是否是嵌套范围,我们递归或简单地返回 join ed 元素。
可悲的是,它不起作用。尝试运行后:
int main() {
std::set<std::vector<std::list<int>>> nested_ranges = {
{{1, 2, 3}, {4, 5}, {6}},
{{7}, {8, 9}, {10, 11, 12}},
{{13}}
};

std::ranges::copy(
rng::deep_flatten(nested_ranges),
std::ostream_iterator<int>(std::cout, " ")
);
}
我收到一条错误消息:
In instantiation of 'auto rng::deep_flatten(Rng&&) [with Rng = std::ranges::join_view<std::ranges::ref_view<std::set<std::vector<std::__cxx11::list<int> > > > >]':
required from 'auto rng::deep_flatten(Rng&&) [with Rng = std::set<std::vector<std::__cxx11::list<int> > >&]'
required from here
error: use of 'auto rng::deep_flatten(Rng&&) [with Rng = std::ranges::join_view<std::ranges::ref_view<std::set<std::vector<std::__cxx11::list<int> > > > >]' before deduction of 'auto'
39 | return deep_flatten(rng | views::join);
| ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~

研究过类似的问题后,我真的不明白为什么这里会出现错误。
我正在使用 gcc version 10.1.0 (Rev3, Built by MSYS2 project)

最佳答案

这里有两个问题。
第一个问题是你的:

namespace rng {
template <std::ranges::range Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;

if constexpr (range<Rng>) { // <==
return deep_flatten(rng | views::join);
} else {
return rng | views::join;
}
}
}
这个函数是无限递归的。 deep_flatten受约束 range<Rng> ,所以 if constexpr检查总是会是真的,所以我们永远不会进入基本情况。这只是一个错误 - 我们正在检查错误的东西,这不是我们是否是一个范围,而是我们的基础值是否是一个范围。那是:
namespace rng {
template <typename Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;

auto joined = rng | views::join;
if constexpr (range<range_value_t<decltype(joined)>>) {
return deep_flatten(joined);
} else {
return joined;
}
}
}
在这里我们进入第二个问题,即标准库的问题。什么 rng | views::join意思是:

The name views​::​join denotes a range adaptor object ([range.adaptor.object]). Given a subexpression E, the expression views​::​join(E) is expression-equivalent to join_­view{E}.


但是 join_view{E}对于 E这已经是 join_view 的专长了...由于类模板参数推导(CTAD),现在是空操作 - 拷贝推导候选是最佳候选,所以我们的嵌套 join操作居然变成了单 join .您最初的实现解决了这个问题,因为它不是 join -ing join_view ,总是 join -ing vector s。
我已提交 LWG 3474 .
同时,我们可以解决 views::join直接使用 join_view 的问题并明确指定模板参数:
namespace rng {
template <typename Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;


auto joined = join_view<views::all_t<Rng>>(rng);

if constexpr (range<range_value_t<decltype(joined)>>) {
return deep_flatten(joined);
} else {
return joined;
}
}
}
这有效。

关于c++ - 使用基于概念的递归函数模板在扣除 'auto [...] ' 之前使用 'auto',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63249315/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com