【发布时间】:2015-09-28 23:24:49
【问题描述】:
transform 执行的函数
const std::vector<int> a = {1, 2, 3, 4, 5};
const std::vector<double> b = {1.2, 4.5, 0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);
transform<Foo> (result.begin(),
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
是在多个容器上对std::transform进行泛化,将结果输出到向量result中。显然需要一个带有签名(int, double, const std::string&) 的函数来处理此示例中的三个容器。但是因为容器有不同的长度,我们需要使用一些重载。我将使用持有者类 Foo 的这些成员重载来测试它:
static int execute (int i, double d, const std::string& s) {return i + d + s.length();}
static int execute (int i, const std::string& s) {return 2 * i + s.length();}
static int execute (int i) {return 3 * i - 1;}
但是,除非我定义其他三个从未调用过的重载,即带有参数(int, double)、(const std::string&) 和(),否则程序将无法编译。我想删除这些重载,但程序不会让我这样做。你可以想象如果我们有超过 3 个容器(不同长度的)这会导致的问题,当它们甚至不被使用时,强制定义具有许多参数排列的重载。
这是我的工作程序,它显然会说明为什么需要这些无关的重载。我不明白如何或为什么要强制定义它们,并且想删除它们。为什么它们必须在那里,以及如何消除对它们的需求?
#include <iostream>
#include <utility>
#include <tuple>
bool allTrue (bool a) {return a;}
template <typename... B>
bool allTrue (bool a, B... b) {return a && allTrue(b...);}
template <typename F, size_t... Js, typename Tuple>
typename F::return_type screenArguments (std::index_sequence<>, std::index_sequence<Js...>, Tuple& tuple) {
return F::execute (*std::get<Js>(tuple)++...);
}
// Thanks to Barry for coming up with screenArguments.
template <typename F, std::size_t I, size_t... Is, size_t... Js, typename Tuple>
typename F::return_type screenArguments (std::index_sequence<I, Is...>, std::index_sequence<Js...>, Tuple& tuple) {
if (std::get<2*I>(tuple) != std::get<2*I+1>(tuple))
return screenArguments<F> (std::index_sequence<Is...>{}, std::index_sequence<Js..., 2*I>{}, tuple);
else
return screenArguments<F> (std::index_sequence<Is...>{}, std::index_sequence<Js...>{}, tuple);
}
template <typename F, typename Tuple>
typename F::return_type passCertainArguments (Tuple& tuple) {
return screenArguments<F> (std::make_index_sequence<std::tuple_size<Tuple>::value / 2>{},
std::index_sequence<>{}, tuple);
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator result, const std::index_sequence<Is...>&, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...);
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = passCertainArguments<F>(tuple);
return result;
}
template <typename F, typename OutputIterator, typename... InputIterators>
OutputIterator transform (OutputIterator result, InputIterators... iterators) {
return transformHelper<F> (result, std::make_index_sequence<sizeof...(InputIterators) / 2>{}, iterators...);
}
// Testing
#include <vector>
struct Foo {
using return_type = int;
static int execute (int i, double d, const std::string& s) {return i + d + s.length();}
static int execute (int i, const std::string& s) {return 2 * i + s.length();}
static int execute (int i) {return 3 * i - 1;}
// These overloads are never called, but apparently must still be defined.
static int execute () {std::cout << "Oveload4 called.\n"; return 0;}
static int execute (int i, double d) {std::cout << "Oveload5 called.\n"; return i + d;}
static int execute (const std::string& s) {std::cout << "Oveload6 called.\n"; return s.length();}
};
int main() {
const std::vector<int> a = {1, 2, 3, 4, 5};
const std::vector<double> b = {1.2, 4.5, 0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);
transform<Foo> (result.begin(),
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
for (double x : result) std::cout << x << ' '; std::cout << '\n';
// 4 11 8 11 14 (correct output)
}
【问题讨论】:
-
不要乱用模板。如果两个容器的类型相同,你会怎么做?
-
因为如果字符串序列比其他两个更长,则需要这些重载。由于序列的长度是运行时属性,因此您无法在编译时知道它们是否需要。 (编译器不需要完整组合的唯一原因是
double可以隐式转换为int。) -
我怀疑是这样的。那么避免这个问题的正确设计是什么?如果这样的解决方案过于艰巨,您至少可以提出另一种方法吗?
-
你能解释一下为什么你首先需要这个吗?整个省略论点的事情似乎很脆弱和可疑。
-
抱歉,只是在练习一些模板编程。随意忽略这个问题,除非你发现它足够有趣来解决它(正确地)。因此,您建议忘记使用重载,并尝试使用多个具有相同签名
(int, double, const std::string&)的 lambda,然后在运行时根据缺少哪些参数调用适当的 lambda?我首先想到了这个,但很快就放弃了这个想法。
标签: c++ templates c++11 variadic