gpt4 book ai didi

c++ - 在使用基于概念的递归功能模板推导 'auto […] '之前使用 'auto'

转载 作者:行者123 更新时间:2023-12-02 10:27:06 36 4
gpt4 key购买 nike

我想创建一个deep_flatten函数模板,该模板将生成包含range ed的元素的join。例如,如果仅考虑嵌套的std::vector,我可以拥有:

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。我认为我可以使用更多 魔术并使用 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}.


但是由于类模板实参推导(CTAD),现在已经是 join_view{E}的特化的 Ejoin_view现在是空操作-复制推导候选者是最佳候选者,因此我们的嵌套 join操作实际上成为单个 join 。您最初的实现解决了这个问题,因为它不是 join -ing join_view,而是总是 join -ing vector
我已提交 LWG 3474
同时,我们可以通过直接使用 views::join并显式指定template参数来解决 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/63908656/

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