C++ C++;11 Lambda函数隐式转换为bool vs.std::function

C++ C++;11 Lambda函数隐式转换为bool vs.std::function,c++,c++11,lambda,overloading,C++,C++11,Lambda,Overloading,考虑以下简单的示例代码: #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" <

考虑以下简单的示例代码:

#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;
}
#包括
#包括
无效f(布尔开关){

std::cout可以将没有捕获的lambda函数转换为常规函数指针,然后将其标准转换为bool

如果通过非常量引用获取
std::function
,那么就不需要将其作为候选函数,因为将lambda转换为
std::function
需要一个临时变量,并且临时变量不能绑定到非常量引用。这只需要将
f(bool)
作为候选变量,因此没有歧义

有很多方法可以避免歧义。例如,可以先创建
std::function
变量:

std::function<double(int)> g = [](int _idx){ return 7.9;};
f(g);
函数g=[](int _idx){return 7.9;}; f(g);
或者你可以投下lambda:

f(std::function<double(int)>([](int _idx){return 7.9;}));
f(std::function([](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;
}  
模板
函数make_函数(T*f){return{f};}
int main(int argc,char*argv[]){
f(make_函数([](int_idx){return 7.9;}));
返回0;
}  
或者您可以抓住您感兴趣的特定功能:

int main ( int argc, char* argv[] ) {
    void (*f_func)(std::function<double(int)>) = f;
    f_func([](int _idx){ return 7.9;});
    return 0;
}
intmain(intargc,char*argv[]){
void(*f_func)(std::function)=f;
f_func([](int_idx){return 7.9;});
返回0;
}
还有一个选项:

模板
无效f(f _f){

std::cout您可以通过创建一个helper类来摆脱隐式转换

#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;
}
#包括
#包括
结构布尔{
布尔州;
布尔(布尔b):状态(b){}
运算符bool(){返回状态;}
};
void f(布尔_开关){
标准::cout
鲍勃是你叔叔


真正的问题是
std::function
的构造函数没有执行类似的测试,而是声明(错误)它可以从任何东西构建。这是标准中的一个缺陷,我怀疑一旦概念标准化,这个缺陷就会被修复。

我觉得重载非模板函数和模板函数通常是一个坏主意,特别是当模板名义上与非模板匹配时。如果您打算调用非模板迟到但您的类型不完全匹配,您将最终调用模板。例如,
f(feof(some_FILE))
将调用模板,即使
feof()
只返回
int
(而不是
bool
)历史上的偶然。@Adam取决于预期用途。如果你想要转换,当然你不会这样做。但是如果你知道你在做什么,这可能是一个非常好的主意。我已经添加了限制,谢谢。我从来没有发现这是一个非常好的主意。IMAO它太脆弱了。你是说在这种情况下,有人调用
f(feof)吗(一些文件))
希望将
int
作为函数处理吗?如果您的
g(int)
重载了
g(T)
,您将获得
未签名的
、一个
字符的模板(有时)“一个
int16\u t
,或者几乎所有正常情况下会被转换成
int
const
转换和派生到基转换的东西,程序员也会经常使用,而不会考虑或意识到,这样的重载集也会破坏它。”Adam I说“如果你想要转换,当然你不会这么做。”这包括你的
f(feof(some_文件))
示例。是的,在这种情况下这是一个非常糟糕的主意。也许你应该使用一些SFINAE魔法来减少贪婪。
std::function
也应该这样做,尽管它不是必须这样做的。重载一个取
std::function
的函数目前不是一个好主意。有一个贪婪的构造函数模板
std::function
在C++11中,不需要(SFINAE-)拒绝“无效”类型的
。也就是说,第二个重载可以匹配任何类型。不过,它随后被列为用户定义的转换。这可能是一个糟糕的解决方案(也可能不是)。但是使用
f([&](int_idx){return 7.9;})
也应该可以,对吗?不,因为lambda函数中没有引用,编译器仍然会将其转换为函数指针。只需使用,例如“argc”,它就不会转换为函数指针。@bmm从来没有想过,谢谢。在coliru上尝试过,但是。@bmm标准使这个相反的存在离子操作符取决于lambda是否有lambda捕获,而不是它是否真的捕获了任何东西。因此,cv_和_he的解决方案应该是可行的(尽管我认为这是一个破解)。如果你也有相同函数的整数重载,例如void f(int_I);当你用f(true)调用它时,很遗憾这会失败,将选择整数重载,并将值强制为1:(
template<typename F>
void f(F _f) {
    std::cout << "Nothing really, too: " << _f(3) << std::endl;
}
#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;
}
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;
}
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";
}