考虑这个简单的示例代码:
#include <functional>
#include <iostream>
void f(bool _switch) {
std::cout << "Nothing really" << std::endl;
}
void f(std::function<double (int)> _f) {
std::cout << "Nothing really, too" << std::endl;
}
int main ( int argc, char* argv[] ) {
f([](int _idx){ return 7.9;});
return 0;
}
无法编译:
$ g++ --std=c++11 main.cpp
main.cpp: In function ‘int main(int, char**)’:
main.cpp:15:33: error: call of overloaded ‘f(main(int, char**)::<lambda(int)>)’ is ambiguous
main.cpp:15:33: note: candidates are:
main.cpp:6:6: note: void f(bool)
main.cpp:10:6: note: void f(std::function<double(int)>)
但是如果我用引用参数替换第二个函数,它编译得很好。如果它被const引用替换它又失败了。
所以我对这个例子有一些疑问:
bool
?答案 0 :(得分:8)
没有捕获的lambda函数可以转换为常规函数指针,然后标准转换为bool。
如果你通过非const引用获取std::function
,那么它将它作为候选者消除,因为将lambda转换为std::function
需要临时,而临时不能绑定到非{ const参考。只留下f(bool)
作为候选人,所以没有歧义。
有很多方法可以避免歧义。例如,您可以先创建std::function
变量:
std::function<double(int)> g = [](int _idx){ return 7.9;};
f(g);
或者你可以施放lambda:
f(std::function<double(int)>([](int _idx){return 7.9;}));
你可以有一个辅助功能:
template<typename T>
std::function<T> make_function(T *f) { return {f}; }
int main ( int argc, char* argv[] ) {
f(make_function([](int _idx){ return 7.9;}));
return 0;
}
或者你可以抓住你感兴趣的特定功能:
int main ( int argc, char* argv[] ) {
void (*f_func)(std::function<double(int)>) = f;
f_func([](int _idx){ return 7.9;});
return 0;
}
答案 1 :(得分:4)
namespace details{
template<class Sig,class=void>
struct invoke {};
template<class F, class...Args>
struct invoke<F(Args...),decltype(void(
std::declval<F>()(std::declval<Args>()...)
))>{
using type=decltype(std::declval<F>()(std::declval<Args>()...));
};
}
template<class Sig>struct invoke:details::invoke<Sig>{};
template<typename Sig, typename T, typename=void>
struct invoke_test:std::false_type {};
template<typename R, typename...Args, typename T>
struct invoke_test<R(Args...), T,
typename std::enable_if<
std::is_convertible<
typename invoke<T(Args...)>::type,
R
>::value
>::type
>:std::true_type {};
template<typename...Args,typename T>
struct invoke_test<void(Args...),T,
decltype( void( typename invoke<T(Args...)>::type ) )
>:std::true_type{};
template<typename Sig, typename T>
constexpr bool invokable() {
return invoke_test<Sig,T>::value;
}
这为我们提供了一个伪概念invokable
。
我们可以像以下一样使用它:
template<typename F>
typename std::enable_if<invokable<double(int),F>()>::type
f(F&&){
std::cout << "can be invoked\n";
}
void f(bool) {
std::cout << "is bool\n";
}
鲍勃是你的叔叔。
真正的问题是std::function<double(int)>
的构造函数没有做类似的测试,而是声称(错误地)它可以从任何东西构造。这是标准中的一个缺陷,我怀疑一旦概念被标准化就会被修复。
答案 2 :(得分:3)
您可以通过创建辅助类
来摆脱隐式转换#include <functional>
#include <iostream>
struct Boolean {
bool state;
Boolean(bool b):state(b){}
operator bool(){ return state; }
};
void f(Boolean _switch) {
std::cout << "Nothing really " << _switch << std::endl;
}
void f(std::function<double (int)> _f) {
std::cout << "Nothing really, too" << std::endl;
}
int main ( int argc, char* argv[] ) {
f([](int _idx){ return 7.9;});
f(true);
return 0;
}
你是否想要用例如f
来打电话。一个指针,并期望它调用第一个重载,你必须将其转换为bool
或者将相应的构造函数/强制转换为辅助类。
答案 3 :(得分:2)
Vaughn Cato's answer的另一个选项:
template<typename F>
void f(F _f) {
std::cout << "Nothing really, too: " << _f(3) << std::endl;
}
现在第二个重载是一个模板,因此它被选择用于lambda(或任何东西),第一个被选择用于bool
。所以调用f
并不比需要复杂。
但是,一个问题是如果你想添加更多的重载,另一个问题是只有与bool
完全匹配时才会调用第一个重载。