我想创建一个方法,它接受一些参数T,布尔函数并对它做一些事情(假设如果func(param)为真则打印它)。 我遇到的问题是:
当你编写没有模板的代码时,以下两个例子都可以工作:
static bool is_even(int i) {
return i % 2 == 0;
}
void f(const int &d, bool (*f) (int)) {
cout << (f(d) ? "true" : "false");
}
//void f(const int &d, std::function<bool(int)> f) {
// cout << (f(d) ? "true" : "false");
//}
f(10, [](int e) -> bool { return e % 2 == 0; });
f(10, is_even);
即使我先注释掉第二个功能并发表评论,它仍然有用。 但是当我添加这样的模板时:
template<typename T>
void f(const T &d, bool (*f) (T)) {
cout << (f(d) ? "true" : "false");
}
或者这个
template<typename T>
void f(const T &d, std::function<bool(T)> f) {
cout << (f(d) ? "true" : "false");
}
它说
no instance of function template "f" matches the argument list argument types are: (int, lambda []bool (int e)->bool)
如果我使用std :: function,它也会说
no instance of function template "f" matches the argument list argument types are: (int, bool (int i))
所以问题是:如何使这个模板兼具函数和lambdas?
编辑: 我想我最初提供的信息少于所需信息。 问题是我想用不同的函数多次重载一个运算符:
template<typename T>
vector<T> operator|(const vector<T> &vec, void (*f) (T)) {
// ...
}
template<typename T>
vector<T> operator|(const vector<T> &vec, bool (*f) (T)) {
// ...
}
template<typename TIn, typename TOut>
vector<TOut> operator|(const vector<TIn> &vec, TOut (*f) (TIn)) {
// ...
}
答案 0 :(得分:3)
问题是在模板参数推断期间不会检查隐式转换。
看起来你想要的是在第二个参数中关闭模板参数推导。您可以通过强制T
处于非推断的上下文中来实现。我们将使用标准中指定的以下非推导上下文。
5未推断的上下文是:
(5.1) - 使用 qualified-id 指定的类型的嵌套名称说明符。
template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;
template<typename T>
void f(const T &d, bool (*g) (identity_t<T>)) {
cout << (g(d) ? "true" : "false");
}
/* or */
template<typename T>
void f(const T &d, std::function<bool(identity_t<T>)> f) {
cout << (f(d) ? "true" : "false");
}
它们都适用于:
f(10, [](int e) -> bool { return e % 2 == 0; });
f(10, is_even);
答案 1 :(得分:2)
你可以这样做:
template<typename T, typename F>
void func(const T &d, F f) {
std::cout << (f(d) ? "true" : "false");
}
答案 2 :(得分:1)
Lambdas和std::function
与函数指针的类型不同,尽管它们可以是可互换的。但是,在模板类型推导中,类型不会被隐式转换,这就是没有有效匹配的原因。
您可以通过明确指定模板类型
来强制类型f<int>(10, [](int e) -> bool { return e % 2 == 0; });
在这种情况下,代码几乎会编译,但是编译器抱怨你有一个模糊的f
定义,因为指针重载是一个同样好的匹配。因此,只保留std::function
重载,并手动指定模板类型
#include <iostream>
#include <functional>
static bool is_even(int i) {
return i % 2 == 0;
}
template<typename T>
void f(const T &d, std::function<bool(T)> f) {
std::cout << (f(d) ? "true" : "false") << std::endl;
}
int main()
{
f<int>(10, [](int e) -> bool { return e % 2 == 0; });
f<int>(10, is_even);
}
答案 3 :(得分:1)
以下是您可以执行此操作的示例:
#include <cmath>
#include <functional>
#include <iomanip>
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T, typename F>
auto func(T && t, F && f)
-> typename enable_if<is_same<bool, typename result_of<F(T)>::type>::value, bool>::type
{
return f(t);
}
bool is_pi(double d)
{
// approximately
return d == 3.1415926;
}
int main()
{
cout << boolalpha << func(42, [](int answer){ return answer == 42; }) << endl;
cout << boolalpha << func(M_PI, is_pi);
return 0;
}
首先允许使用各种函数指针,函数对象或lambda。然后,enable_if决定是否可以为给定类型实例化模板,具体取决于它是否支持类型为T的参数并返回bool。
见这里:ideone
答案 4 :(得分:0)
由于您使用的是lambda,我建议您使用template<typename T>
void f(const T&, std::function<bool(T)>)
定义,如果您愿意的话,这将允许您支持捕捉lambdas。
你的实现很好,你只需要通过指定而不是期望它推导T
来帮助C ++。此代码在Visual Studio 2013和gcc 4.9.2中可以正常工作:
#include <iostream>
#include <functional>
using namespace std;
static bool is_even(int i) {
return i % 2 == 0;
}
template<typename T>
void f(const T &d, function<bool(T)> f) {
cout << (f(d) ? "true" : "false");
}
int main() {
f<int>(10, [](int e){ return e % 2 == 0; });
f<int>(10, is_even);
return 0;
}
请注意,我正在指定模板类型int
。问题是C ++不知道T
是否应基于您的第一个或第二个参数。如果它们是相同类型,则可以,但编译器无法推导出std::function
的参数类型,因此无法确认类型T
是否一致。通过传入类型,编译器不再需要推导它。
答案 5 :(得分:0)
我知道您将问题简化到建议的解决方案不再适用于原始问题的情况,因此这里有一个适用于您原始问题的解决方案:
你想要做的是你想要在编译时找到传递给你的函数的任何返回类型(无论是lambda还是函数指针),然后根据该类型做不同的事情。你可以通过引入一个找到返回类型的中间函数来实现它,然后调用实际函数。
template<typename T>
void real_foo(const vector<T> &vec, void (*f)(T)) {
cout << "void";
}
template<typename T>
void real_foo(const vector<T> &vec, bool (*f)(T)) {
cout << "bool";
}
template<typename TIn, typename TOut>
void real_foo(const vector<TIn> &vec, TOut (*f)(TIn)) {
cout << "generic";
}
template<typename TIn, typename F>
void operator|(const vector<TIn> &vec, F &&f) {
using TOut = decltype(f(declval<TIn>()));
TOut (*fp)(TIn) = f;
real_foo(vec, fp);
}
如果你想将不可转换的lambda传递给函数指针(例如,如果它们捕获了某些东西),你需要更复杂一些(注意如果你是的话,你不需要使用std::function
只执行lambda而不存储它。)
template <typename TIn, typename F, typename TOut>
struct Foo {
static void foo(const vector<TIn> &vec, F &&f) {
cout << "generic";
}
};
template <typename TIn, typename F>
struct Foo<TIn, F, void> {
static void foo(const vector<TIn> &vec, F &&f) {
cout << "void";
}
};
template <typename TIn, typename F>
struct Foo<TIn, F, bool> {
static void foo(const vector<TIn> &vec, F &&f) {
cout << "bool";
}
};
template<typename TIn, typename F>
void operator|(const vector<TIn> &vec, F &&f) {
using TOut = decltype(f(declval<TIn>()));
Foo<TIn, F, TOut>::foo(vec, forward<F>(f));
}
现在您可以按照您设想的方式使用运算符:
vec | [](int){};
vec | [](int){ return true; };
vec | [](int){ return 5; };
vec | is_even;