C++ 使用std::函数进行重载解析

C++ 使用std::函数进行重载解析,c++,c++11,lambda,overloading,C++,C++11,Lambda,Overloading,考虑以下代码示例: #include <iostream> #include <functional> typedef std::function<void()> func1_t; typedef std::function<void(int)> func2_t; struct X { X (func1_t f) { } X (func2_t f) { } }; int main ( ) { X x([](){

考虑以下代码示例:

#include <iostream>
#include <functional>

typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;

struct X
{
   X (func1_t f)
   { }

   X (func2_t f)
   { }
};

int main ( )
{
   X x([](){ std::cout << "Hello, world!\n"; });
}
#包括
#包括
typedef std::函数func1_t;
typedef std::函数func2_t;
结构X
{
X(函数1\u t f)
{ }
X(函数2\u t f)
{ }
};
int main()
{

X([]){std::cout它是完全有效的。因为c++11 lambda表达式(以及您的
std::function
包装器)创建函数对象。函数对象的强大之处在于,即使它们是泛型的,它们仍然是一流的对象。与普通函数模板不同,它们可以传递给函数,也可以从函数返回

您可以通过继承和使用声明显式创建运算符重载集

模板
结构重载集:F1,F2
{
重载集(F1-x1,F2-x2):F1(x1),F2(x2){
使用F1::operator();
使用F2::运算符();
};
模板
过载设置过载(F1-x1,F2-x2)
{
返回过载设置(x1,x2);
}
自动f=过载(
[](){return 1;},
[](int x){返回x+1;}
);
int x=f();
int y=f(2);

编辑:如果在提供的示例中替换

F1 -> std::function<void()> 
F2 -> std::function<void(int)>
F1->std::function
F2->标准::函数

模板化的解决方案只是为了证明概念可以扩展到通用代码,并且可以消除歧义

在您的情况下,当使用gcc 4.7等较旧的编译器时,您可以通过显式强制转换和

以防万一,在C++11中,如果换成另一种方式(尝试将接受int的lambda转换为不接受参数的std::函数等等),它将不起作用。

让我们看一下
std::function
(它接受任何可调用的)的构造函数模板的规范:[func.wrap.func.con]/7-10

7要求:
F
可复制

8备注:这些施工人员不得参与超载 解析,除非参数类型的
f
是可调用的(20.9.11.2)
ArgTypes…
和返回类型
R

[……]

“不得参与过载解决”对应于SFINAE的拒绝。净效果是,如果您有一组过载功能
foo

void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
然后,第二个重载
foo
被明确选择:因为
std::function
F
定义为其外部接口,所以
F
定义了哪些参数类型被传递到
std::function
。然后,必须使用这些参数(参数类型)调用包装的函数对象。如果一个
double
被传递到
std::function
,它就不能被传递到一个使用
std::string
的函数,因为没有转换
double
->
std::string
。 对于
foo
的第一个重载,参数
[](std::string){}
因此被认为不可调用
std::function
。构造函数模板被停用,因此没有从
[](std::string){}进行可行的转换
std::function
。第一个重载将从用于解析调用(C)的重载集中删除,只留下第二个重载

请注意,上面的措辞有一点变化,原因是:有一个例外,如果
std::function
的返回类型
R
void
,那么任何返回类型都会被接受(并丢弃)用于上述构造函数模板中的可调用项。例如,
[]()->void{}
[]()->bool{}
可为
std::function
调用。因此,以下情况会产生歧义:

void foo(std::function<void()>);
void foo(std::function<bool()>);

foo([]() -> bool {}); // ambiguous
类似地,在转换构造函数上使用SFINAE可以使转换不可行:

#include <type_traits>
#include <iostream>

struct foo
{
    template<class T, class =
             typename std::enable_if< std::is_integral<T>::value >::type >
    foo(T)
    {  std::cout << "foo(T)\n";  }
};

struct bar
{
    template<class T, class =
             typename std::enable_if< not std::is_integral<T>::value >::type >
    bar(T)
    {  std::cout << "bar(T)\n";  }
};

struct kitty
{
    kitty(foo) {}
    kitty(bar) {}
};

int main()
{
    kitty cat(42);
    kitty tac(42.);
}
#包括
#包括
结构foo
{
模板::类型>
傅(T)
{std::cout
巴(吨)

{std::cout标准规定,对于
模板函数::函数(F)
重载类型
F
必须可以用
Args
中类型的参数调用。我假设这意味着标准要求构造函数必须是SFINAE(如果不能),这意味着您的调用是明确的。@简单:是的,这是有意义的,但我不认为转换为参数SFINAE失败会有助于重载解析。@Simple[res.on.required]“违反函数的Requires:段落中指定的前提条件会导致未定义的行为,除非函数的Throws:段落指定在违反前提条件时引发异常。”SFINAE失败在备注:部分中明确提到,例如:。[syserr.errcode.constructor]/7@lisyarus[参见前面的评论]…从技术上讲,它是UB(不是格式错误);libstdc++接受它并做正确的事情。我很困惑。为什么
X(func1\u t f)
显然不应该是匹配的,而
X(func2\u t f)
显然不匹配?在我的示例中没有重载lambda对象
X([])([]){std::cout哪里可以看到重载lambda运算符()?重载的不是lambdas运算符。是构造函数重载了第一类对象参数。然后是其中一个(
std::function”),然后是其中一个(
std::function
)匹配闭包类型
[]()
“在哪种意义上匹配?
[]{}的闭包类型”
是一个独特的未命名类,与
std::function
无关。后者有一个转换构造函数,它接受满足返回类型
void
Callable
要求且没有参数的任何参数表达式。我认为sfinae只处理函数的签名有效/无效,但她的e是从参数转换而来。它是否也涉及sfinae?我不明白为什么[res.on.required]
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
foo([](std::string){}) // (C)
void foo(std::function<void()>);
void foo(std::function<bool()>);

foo([]() -> bool {}); // ambiguous
#include <type_traits>
#include <iostream>

template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{  std::cout << "foo 1\n";  }

template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{  std::cout << "foo 2\n";  }

int main()
{
    foo(42);
    foo(42.);
}
#include <type_traits>
#include <iostream>

struct foo
{
    template<class T, class =
             typename std::enable_if< std::is_integral<T>::value >::type >
    foo(T)
    {  std::cout << "foo(T)\n";  }
};

struct bar
{
    template<class T, class =
             typename std::enable_if< not std::is_integral<T>::value >::type >
    bar(T)
    {  std::cout << "bar(T)\n";  }
};

struct kitty
{
    kitty(foo) {}
    kitty(bar) {}
};

int main()
{
    kitty cat(42);
    kitty tac(42.);
}