如何使用C ++模板调用匹配的函数?例如,如果我有函数a和b:
void a_impl(string, int){}
void b_impl(int, string){}
template<typename X, typename Y>
void a(X x, Y y){
a_impl(x, y);
}
template<typename X, typename Y>
void b(X x, Y y){
b_impl(x, y);
}
template<typename X, typename Y>
void a_or_b(X x, Y y);
如何实施a_or_b
,如果匹配则调用a(x, y)
,否则请致电b(x, y)
?
我尝试制作的是for_each
函数,可以处理这些情况:
vector<pair<string, int>> v1 = {{"one", 1}, {"two", 2}};
for_each(v1, [](string x, int y){
cout << x << " " << y << endl;
});
vector<int> v2 = {1, 2, 3};
for_each(v2, [](int x){
cout << x << endl;
});
到目前为止,我已独立地为元组和单个变量工作,但我希望自动选择适当的版本。这是我到目前为止的实施情况; unpack
apply_from_tuple
来自此页面http://www.cppsamples.com/common-tasks/apply-tuple-to-function.html。{/ p>
template<typename Range, typename Func>
void for_each_unpack(Range && range, Func && func){
for (auto && element : range){
using Element = decltype(element);
unpack(std::forward<Func>(func), std::forward<Element>(element));
}
}
template<typename Range, typename Func>
void for_each_nounpack(Range && range, Func && func){
for (auto && element : range){
using Element = decltype(element);
std::forward<Func>(func)(std::forward<Element>(element));
}
}
编辑:感谢@jotik让它工作。我把代码放在github上https://github.com/csiz/for_each。
答案 0 :(得分:3)
使用包含decltype
和SFINAE的结尾回复类型:
#include <iostream>
#include <string>
#include <utility>
void a(std::string, int) { std::cout << "a" << std::endl; }
void b(int, std::string) { std::cout << "b" << std::endl; }
template <typename ... Args>
auto a_or_b(Args && ... args)
-> decltype(a(std::forward<Args>(args)...))
{ return a(std::forward<Args>(args)...); }
template <typename ... Args>
auto a_or_b(Args && ... args)
-> decltype(b(std::forward<Args>(args)...))
{ return b(std::forward<Args>(args)...); }
int main() {
std::string s;
int i;
a_or_b(s, i); // calls a
a_or_b(i, s); // calls b
}
我在上面的示例中使用了完美转发,因为它避免了每个参数的副本,但是使用显式类型的不太通用的天真解决方案也有效:
template <typename X, typename Y>
auto a_or_b(X x, Y y) -> decltype(a(x, y))
{ return a(x, y); }
template <typename X, typename Y>
auto a_or_b(X x, Y y) -> decltype(b(x, y))
{ return b(x, y); }
SFINAE在这种情况下的工作原理如下。请注意,a_or_b
有2个模板定义。当您向a_or_b
编写函数调用时,编译器会尝试确定您要调用的a_or_b
。由于SFINAE,它会忽略任何无法推断出其类型的模板a_or_b
。例如。对于调用a_or_b(s, i);
,第二个decltype(b(std::forward<Args>(args)...))
定义的(尾随)返回类型a_or_b
不起作用,因此编译器不会考虑第二个a_or_b
定义。
在这种情况下,返回类型必须是尾随返回类型,因为它取决于函数参数。例如,以下内容无法编译:
template <typename ... Args>
decltype(b(std::forward<Args>(args)...)) a_or_b(Args && ... args)
{ return b(std::forward<Args>(args)...); }
答案 1 :(得分:1)
这是你想法的有趣变化。传递一组函数和一组参数。然后调用该元组中的所有函数(从左到右),它们可以接受那些给定的参数。此外,存储元组中调用的所有返回值(如果有)。在这里,我使用SFINAE中的void_t
(C ++ 17中的std::void_t
)方面:
#include <iostream>
#include <tuple>
#include <type_traits>
template <typename T>
using void_t = void;
template <std::size_t N, typename VoidTFailed, typename EnableIfFailed, typename Tuple, typename... Args>
struct Try {
static bool execute (Tuple&&, Args&&...) {
std::cout << "No function found.\n";
return false;
}
};
template <std::size_t N, typename VoidTFailed, typename Tuple, typename... Args>
struct Try<N, VoidTFailed, std::enable_if_t<(N < std::tuple_size<Tuple>::value)>, Tuple, Args...> : std::conditional_t<(N < std::tuple_size<Tuple>::value - 1),
Try<N+1, void, void, Tuple, Args...>,
Try<N+1, int, int, Tuple, Args...> // If N == std::tuple_size<Tuple>::value - 1, then simply inherit from the primary template above, as inheriting from Try<N+1, void, void, Tuple, Args...> will cause a compiling error when std::tuple_element_t<N+1, Tuple> is instantiated in the specialization below.
> {};
struct NoReturnValue {
friend std::ostream& operator<< (std::ostream& os, const NoReturnValue&) {
return os << "[no value returned]";
}
};
template <std::size_t N, typename Tuple, typename EnableIfFailed, typename... Args>
struct Continue {
static auto execute (Tuple&& functionTuple, Args&&... args) {
const auto singleTuple = std::make_tuple (std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...));
return std::tuple_cat (singleTuple, Try<N+1, void, void, Tuple, Args...>::execute(std::forward<Tuple>(functionTuple), std::forward<Args>(args)...));
}
};
template <std::size_t N, typename Tuple, typename... Args> // This specialization is only used if the function in question returns void.
struct Continue<N, Tuple, std::enable_if_t<std::is_void<decltype(std::declval<std::tuple_element_t<N, std::decay_t<Tuple>>>()(std::declval<std::decay_t<Args>>()...))>::value>, Args...> {
static auto execute (Tuple&& functionTuple, Args&&... args) {
std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...);
return std::tuple_cat (std::make_tuple(NoReturnValue{}), Try<N+1, void, void, Tuple, Args...>::execute(std::forward<Tuple>(functionTuple), std::forward<Args>(args)...));
}
};
template <std::size_t N, typename Tuple, typename EnableIfFailed, typename... Args>
struct Stop {
static auto execute (Tuple&& functionTuple, Args&&... args) {
return std::make_tuple (std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...));
}
};
template <std::size_t N, typename Tuple, typename... Args> // This specialization is only used if the function in question returns void.
struct Stop<N, Tuple, std::enable_if_t<std::is_void<decltype(std::declval<std::tuple_element_t<N, std::decay_t<Tuple>>>()(std::declval<std::decay_t<Args>>()...))>::value>, Args...> {
static auto execute (Tuple&& functionTuple, Args&&... args) {
std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...);
return std::make_tuple(NoReturnValue{});
}
};
template <std::size_t N, typename Tuple, typename... Args>
struct Try<N, void_t<decltype(std::declval<std::tuple_element_t<N, Tuple>>()(std::declval<Args>()...))>,
std::enable_if_t<(N < std::tuple_size<Tuple>::value)>, Tuple, Args...> : std::conditional_t<(N < std::tuple_size<Tuple>::value - 1),
Continue<N, Tuple, void, Args...>,
Stop<N, Tuple, void, Args...>
> {};
template <typename Tuple, typename... Args>
auto executeAllPossible (Tuple&& functionTuple, Args&&... args) {
return Try<0, void, void, Tuple, Args...>::execute(std::forward<Tuple>(functionTuple), std::forward<Args>(args)...);
}
// Testing
int foo (int, char, bool, int, int) {std::cout << "foo\n"; return 8;}
bool bar (int, char, bool, double, long, bool) {std::cout << "bar\n"; return true;}
void goo (int, char&&, bool, float, int) {std::cout << "goo\n";}
double baz (int, const char, bool&&, double, std::size_t) {std::cout << "baz\n"; return 4.5;}
template <typename Ch, typename Tr, typename Tuple, std::size_t... Is>
void print_tuple_impl (std::basic_ostream<Ch, Tr>& os, const Tuple& t, std::index_sequence<Is...>) {
using A = int[];
(void)A{(void(os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), 0)...};
}
template <typename Ch, typename Tr, typename... Args>
decltype(auto) operator<< (std::basic_ostream<Ch, Tr>& os, const std::tuple<Args...>& t) {
os << "tuple{";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << "}";
}
int main() {
const auto tuple = executeAllPossible (std::make_tuple(foo, bar, goo, baz), 2, 'c', true, 3.5, 8);
std::cout << std::boolalpha << "output = " << tuple << '\n';
}
输出:
foo goo baz
output = tuple{8, [no value returned], 4.5}