无法删除不需要的重载
Cannot remove unwanted overloads
执行的功能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&)
的函数来处理此示例中的三个容器。但是因为容器有不同的长度,我们反而需要使用一些重载。我将使用 holder class 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)
}
编译器在编译时不知道在运行时会使用哪些函数组合。所以你必须为每个组合实现所有 2^N
函数。此外,当您拥有相同类型的容器时,您的方法将不起作用。
如果你想坚持使用模板,我的想法是实现这样的功能:
template <bool Arg1, bool Arg2, bool Arg3>
static int execute (int *i, double *d, const std::string *s);
模板参数Arg1, Arg2, Arg3
表示每个参数的有效性。编译器将自动为每个参数组合生成所有 2^N
实现。在此函数中随意使用 if
语句而不是模板特化 - 它们将在编译时解析为 if (true)
或 if (false)
.
我想我明白了!根据实际需要的参数个数模板化函数Foo::execute
,让它们都有相同的参数:
struct Foo {
using return_type = int;
template <std::size_t> static return_type execute (int, double, const std::string&);
};
template <> Foo::return_type Foo::execute<3> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<2> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<1> (int i, double, const std::string&) {return 3 * i - 1;}
template <> Foo::return_type Foo::execute<0> (int, double, const std::string&) {return 0;} // The only redundant specialization that needs to be defined.
这是完整的解决方案。
#include <iostream>
#include <utility>
#include <tuple>
#include <iterator>
bool allTrue (bool b) {return b;}
template <typename... Bs>
bool allTrue (bool b, Bs... bs) {return b && allTrue(bs...);}
template <typename F, std::size_t N, typename Tuple, typename... Args>
typename F::return_type countArgumentsNeededAndExecute (Tuple&, const std::index_sequence<>&, Args&&... args) {
return F::template execute<N>(std::forward<Args>(args)...);
}
template <typename F, std::size_t N, typename Tuple, std::size_t I, size_t... Is, typename... Args>
typename F::return_type countArgumentsNeededAndExecute (Tuple& tuple, const std::index_sequence<I, Is...>&, Args&&... args) { // Pass tuple by reference, because its iterator elements will be modified (by being incremented).
return (std::get<2*I>(tuple) != std::get<2*I + 1>(tuple)) ?
countArgumentsNeededAndExecute<F, N+1> (tuple, std::index_sequence<Is...>{}, std::forward<Args>(args)..., // The number of arguments to be used increases by 1.
*std::get<2*I>(tuple)++) : // Pass the value that will be used and increment the iterator.
countArgumentsNeededAndExecute<F, N> (tuple, std::index_sequence<Is...>{}, std::forward<Args>(args)...,
typename std::iterator_traits<typename std::tuple_element<2*I, Tuple>::type>::value_type{}); // Pass the default value (it will be ignored anyway), and don't increment the iterator. Hence, the number of arguments to be used does not change.
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator result, const std::index_sequence<Is...>& indices, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...); // Cannot be const, as the iterators are being incremented.
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = countArgumentsNeededAndExecute<F, 0> (tuple, indices); // Start the count at 0. Examine 'indices', causing the count to increase one by one.
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;
template <std::size_t> static return_type execute (int, double, const std::string&);
};
// Template the function Foo::execute according to the number of arguments that are actually needed:
template <> Foo::return_type Foo::execute<3> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<2> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<1> (int i, double, const std::string&) {return 3 * i - 1;}
template <> Foo::return_type Foo::execute<0> (int, double, const std::string&) {return 0;} // The only redundant specialization that needs to be defined.
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)
}
第二个解决方案使用 Andrey Nasonov 的 bool 模板来生成所需的所有 2^N 重载。请注意,上面的解决方案只需要 N+1 个重载模板实例。
#include <iostream>
#include <utility>
#include <tuple>
bool allTrue (bool b) {return b;}
template <typename... Bs>
bool allTrue (bool b, Bs... bs) {return b && allTrue(bs...);}
template <bool...> struct BoolPack {};
template <typename F, typename Tuple, bool... Bs, typename... Args>
typename F::return_type checkArgumentsAndExecute (const Tuple&, const std::index_sequence<>&, BoolPack<Bs...>, Args&&... args) {
return F::template execute<Bs...>(std::forward<Args>(args)...);
}
template <typename F, typename Tuple, std::size_t I, size_t... Is, bool... Bs, typename... Args>
typename F::return_type checkArgumentsAndExecute (Tuple& tuple, const std::index_sequence<I, Is...>&, BoolPack<Bs...>, Args&&... args) { // Pass tuple by reference, because its iterators elements will be modified (by being incremented).
return (std::get<2*I>(tuple) != std::get<2*I+1>(tuple)) ?
checkArgumentsAndExecute<F> (tuple, std::index_sequence<Is...>{}, BoolPack<Bs..., true>{}, std::forward<Args>(args)...,
*std::get<2*I>(tuple)++) : // Pass the value that will be used and increment the iterator.
checkArgumentsAndExecute<F> (tuple, std::index_sequence<Is...>{}, BoolPack<Bs..., false>{}, std::forward<Args>(args)...,
typename std::iterator_traits<typename std::tuple_element<2*I, Tuple>::type>::value_type{}); // Pass the default value (it will be ignored anyway), and don't increment the iterator.
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator& result, const std::index_sequence<Is...>& indices, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...); // Cannot be const, as the iterators are being incremented.
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = checkArgumentsAndExecute<F> (tuple, indices, BoolPack<>{});
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;
template <bool B1, bool B2, bool B3> static return_type execute (int, double, const std::string&) {return 0;} // All necessary overloads defined at once here.
};
// Specializations of Foo::execute<B1,B2,B3>(int, double, const std::string&) that will actually be called by transform<Foo> (it is the client's responsibility to define these overloads based on the containers passed to transform<Foo>).
template <> Foo::return_type Foo::execute<true, true, true> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<true, false, true> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<true, false, false> (int i, double, const std::string&) {return 3 * i - 1;}
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)
}
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&)
的函数来处理此示例中的三个容器。但是因为容器有不同的长度,我们反而需要使用一些重载。我将使用 holder class 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)
}
编译器在编译时不知道在运行时会使用哪些函数组合。所以你必须为每个组合实现所有 2^N
函数。此外,当您拥有相同类型的容器时,您的方法将不起作用。
如果你想坚持使用模板,我的想法是实现这样的功能:
template <bool Arg1, bool Arg2, bool Arg3>
static int execute (int *i, double *d, const std::string *s);
模板参数Arg1, Arg2, Arg3
表示每个参数的有效性。编译器将自动为每个参数组合生成所有 2^N
实现。在此函数中随意使用 if
语句而不是模板特化 - 它们将在编译时解析为 if (true)
或 if (false)
.
我想我明白了!根据实际需要的参数个数模板化函数Foo::execute
,让它们都有相同的参数:
struct Foo {
using return_type = int;
template <std::size_t> static return_type execute (int, double, const std::string&);
};
template <> Foo::return_type Foo::execute<3> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<2> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<1> (int i, double, const std::string&) {return 3 * i - 1;}
template <> Foo::return_type Foo::execute<0> (int, double, const std::string&) {return 0;} // The only redundant specialization that needs to be defined.
这是完整的解决方案。
#include <iostream>
#include <utility>
#include <tuple>
#include <iterator>
bool allTrue (bool b) {return b;}
template <typename... Bs>
bool allTrue (bool b, Bs... bs) {return b && allTrue(bs...);}
template <typename F, std::size_t N, typename Tuple, typename... Args>
typename F::return_type countArgumentsNeededAndExecute (Tuple&, const std::index_sequence<>&, Args&&... args) {
return F::template execute<N>(std::forward<Args>(args)...);
}
template <typename F, std::size_t N, typename Tuple, std::size_t I, size_t... Is, typename... Args>
typename F::return_type countArgumentsNeededAndExecute (Tuple& tuple, const std::index_sequence<I, Is...>&, Args&&... args) { // Pass tuple by reference, because its iterator elements will be modified (by being incremented).
return (std::get<2*I>(tuple) != std::get<2*I + 1>(tuple)) ?
countArgumentsNeededAndExecute<F, N+1> (tuple, std::index_sequence<Is...>{}, std::forward<Args>(args)..., // The number of arguments to be used increases by 1.
*std::get<2*I>(tuple)++) : // Pass the value that will be used and increment the iterator.
countArgumentsNeededAndExecute<F, N> (tuple, std::index_sequence<Is...>{}, std::forward<Args>(args)...,
typename std::iterator_traits<typename std::tuple_element<2*I, Tuple>::type>::value_type{}); // Pass the default value (it will be ignored anyway), and don't increment the iterator. Hence, the number of arguments to be used does not change.
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator result, const std::index_sequence<Is...>& indices, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...); // Cannot be const, as the iterators are being incremented.
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = countArgumentsNeededAndExecute<F, 0> (tuple, indices); // Start the count at 0. Examine 'indices', causing the count to increase one by one.
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;
template <std::size_t> static return_type execute (int, double, const std::string&);
};
// Template the function Foo::execute according to the number of arguments that are actually needed:
template <> Foo::return_type Foo::execute<3> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<2> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<1> (int i, double, const std::string&) {return 3 * i - 1;}
template <> Foo::return_type Foo::execute<0> (int, double, const std::string&) {return 0;} // The only redundant specialization that needs to be defined.
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)
}
第二个解决方案使用 Andrey Nasonov 的 bool 模板来生成所需的所有 2^N 重载。请注意,上面的解决方案只需要 N+1 个重载模板实例。
#include <iostream>
#include <utility>
#include <tuple>
bool allTrue (bool b) {return b;}
template <typename... Bs>
bool allTrue (bool b, Bs... bs) {return b && allTrue(bs...);}
template <bool...> struct BoolPack {};
template <typename F, typename Tuple, bool... Bs, typename... Args>
typename F::return_type checkArgumentsAndExecute (const Tuple&, const std::index_sequence<>&, BoolPack<Bs...>, Args&&... args) {
return F::template execute<Bs...>(std::forward<Args>(args)...);
}
template <typename F, typename Tuple, std::size_t I, size_t... Is, bool... Bs, typename... Args>
typename F::return_type checkArgumentsAndExecute (Tuple& tuple, const std::index_sequence<I, Is...>&, BoolPack<Bs...>, Args&&... args) { // Pass tuple by reference, because its iterators elements will be modified (by being incremented).
return (std::get<2*I>(tuple) != std::get<2*I+1>(tuple)) ?
checkArgumentsAndExecute<F> (tuple, std::index_sequence<Is...>{}, BoolPack<Bs..., true>{}, std::forward<Args>(args)...,
*std::get<2*I>(tuple)++) : // Pass the value that will be used and increment the iterator.
checkArgumentsAndExecute<F> (tuple, std::index_sequence<Is...>{}, BoolPack<Bs..., false>{}, std::forward<Args>(args)...,
typename std::iterator_traits<typename std::tuple_element<2*I, Tuple>::type>::value_type{}); // Pass the default value (it will be ignored anyway), and don't increment the iterator.
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator& result, const std::index_sequence<Is...>& indices, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...); // Cannot be const, as the iterators are being incremented.
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = checkArgumentsAndExecute<F> (tuple, indices, BoolPack<>{});
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;
template <bool B1, bool B2, bool B3> static return_type execute (int, double, const std::string&) {return 0;} // All necessary overloads defined at once here.
};
// Specializations of Foo::execute<B1,B2,B3>(int, double, const std::string&) that will actually be called by transform<Foo> (it is the client's responsibility to define these overloads based on the containers passed to transform<Foo>).
template <> Foo::return_type Foo::execute<true, true, true> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<true, false, true> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<true, false, false> (int i, double, const std::string&) {return 3 * i - 1;}
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)
}