我有一个用于std::vector的平台图的实现。然而,我发现推断返回类型的重复是丑陋的。而且,总的来说,我怀疑实施可能更干净,或者更笼统。
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn) -> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};它可以这样使用:
TEST_F(MarkdownUtilTests, testFlatmap) {
std::vector<int> v = { 1, 2, 3};
std::vector<int> result = flatmap(v, [](int x) { return std::vector<int> { x, x*2, x*3 }; } );
assertThat(result, is(std::vector<int> {1,2,3,2,4,6,3,6,9} ));
}发布于 2016-03-17 00:36:25
标准库中的算法使用迭代器而不是直接处理容器是有原因的。其中之一是它使这样的代码变得非常简单。例如,使用迭代器,我们可以编写类似于以下内容的Flatmap:
template <typename InIter, typename OutIter, typename FN>
void Flatmap(InIter begin, InIter end, OutIter out, FN fn) {
for (; begin != end; ++begin) {
auto y = fn(*begin);
std::copy(std::begin(y), std::end(y), out);
}
}这有点更通用,因为它可以将输入从/写输出接收到本质上的任何迭代器,而不一定要从一个向量读取并生成另一个向量。同样,fn产生的东西也不一定是向量--它本质上可以是任何容器(更准确地说,我们可以使用std::begin()和std::end() )。虽然向量是常见的,但使用其他容器是有原因的--有时甚至有理由写到不完全是容器的东西上。例如:
std::vector<int> inputs { 1, 2, 3 };
Flatmap(inputs.begin(), inputs.end(),
std::ostream_iterator<int>(std::cout, "\t"),
[](auto x) { return std::vector<decltype(x)> { x, x * 2, x * 3 }; });注意,我在lambda中使用了auto作为参数类型,所以这个测试代码需要C++14,但是Flatmap本身在C++11中很好。
如果您想要有更多的前瞻性思考,您可以编写它来处理范围而不是迭代器。埃里克·内布勒的范围库相当不错,而且在即将推出的C++标准中似乎还不错。
随着代码更简单、更通用,另一个明显的优点是它遵循与标准库代码相同的样式--例如,它的签名和使用与std::transform本质上是相同的。
https://codereview.stackexchange.com/questions/123053
复制相似问题