std::function<>
是几乎所有可调用事物的有用包装器,包括自由函数,lambdas,仿函数,成员函数,std::bind
的结果。但是,在创建std::function<>
时,必须明确指定函数签名,如
(取自here)
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
void print_num(int i)
{ std::cout << i << '\n'; }
struct PrintNum {
void operator()(int i) const
{ std::cout << i << '\n'; }
};
// store a free function
std::function<void(int)> f_display = print_num;
// store a lambda
std::function<void()> f_display_42 = []() { print_num(42); };
// store the result of a call to std::bind
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
// store a call to a member function
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
// store a call to a member function and object
using std::placeholders::_1;
std::function<void(int)> f_add_display2= std::bind( &Foo::print_add, foo, _1 );
// store a call to a member function and object ptr
std::function<void(int)> f_add_display3= std::bind( &Foo::print_add, &foo, _1 );
// store a call to a function object
std::function<void(int)> f_display_obj = PrintNum();
即使签名可以从指定的对象推断出来。似乎避免这种情况的一种自然方式(在严格模板化的代码中应该非常方便)是一个重载的函数模板make_function
(在精神上类似于std::make_pair
或std::make_tuple
),当例子只会变成
// store a free function
auto f_display = make_function(print_num);
// store a lambda
auto f_display_42 = make_function([](){ print_num(42);});
// store the result of a call to std::bind
auto f_display_31337 = make_function(std::bind(print_num, 31337));
// store a call to a member function
auto f_add_display = make_function(&Foo::print_add);
// store a call to a member function and object
using std::placeholders::_1;
auto f_add_display2 = make_function(std::bind( &Foo::print_add, foo, _1));
// store a call to a member function and object ptr
auto f_add_display3 = make_function(std::bind( &Foo::print_add, &foo, _1));
// store a call to a function object
auto f_display_obj = make_function(PrintNum());
另一个可能的用例是获取任何类型的可调用对象的返回类型
decltype(make_function(function_object))::return_type;
避免Piotr S.回答this question的答案中的特质魔力。
所以,我的问题:为什么标准不提供此功能?可以在没有编译魔术的情况下实现make_function
吗?还是需要编译魔术? (即便如此,第一个问题仍然存在。)
答案 0 :(得分:14)
class multi_functor
{
public:
void operator()(int) { std::cout << "i'm int" << std::endl }
void operator()(double) { std::cout << "i'm double" << std::endl }
};
int main(void)
{
auto func = make_function(multi_functor());
}
因为这里func
的类型是什么?
这种歧义适用于所有函子对象(包括bind
返回值和lambdas),这将使make_function
仅在函数指针上可用。
答案 1 :(得分:6)
正如此处和其他地方所评论的那样,存在可能会混淆类型推理的歧义问题。可能这些极端情况阻止了std::make_function
被采用,因为它无法解决模糊,重载或与C ++自动类型转换很好地协作。我看到很多反对它的另一个论点是std::function
有开销(在类型擦除中),并且很多人反对在此基础上使用std::function
除了存储可调用的内容之外的任何其他内容。
但是,对于非模糊的情况,可以为lambdas和其他负责类型推理的callables写一个make_function
,这样可以避免在没有歧义的情况下重复函数类型签名。一种方法(取自my related question)如下:
#include <functional>
#include <utility>
#include <iostream>
#include <functional>
using namespace std;
// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)> {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
static typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
//handles bind & multiple function call operator()'s
template<typename ReturnType, typename... Args, class T>
auto make_function(T&& t)
-> std::function<decltype(ReturnType(t(std::declval<Args>()...)))(Args...)>
{return {std::forward<T>(t)};}
//handles explicit overloads
template<typename ReturnType, typename... Args>
auto make_function(ReturnType(*p)(Args...))
-> std::function<ReturnType(Args...)> {
return {p};
}
//handles explicit overloads
template<typename ReturnType, typename... Args, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...))
-> std::function<ReturnType(Args...)> {
return {p};
}
// testing
using namespace std::placeholders;
int foo(int x, int y, int z) { return x + y + z;}
int foo1(int x, int y, int z) { return x + y + z;}
float foo1(int x, int y, float z) { return x + y + z;}
int main () {
//unambuiguous
auto f0 = make_function(foo);
auto f1 = make_function([](int x, int y, int z) { return x + y + z;});
cout << make_function([](int x, int y, int z) { return x + y + z;})(1,2,3) << endl;
int first = 4;
auto lambda_state = [=](int y, int z) { return first + y + z;}; //lambda with states
cout << make_function(lambda_state)(1,2) << endl;
//ambuiguous cases
auto f2 = make_function<int,int,int,int>(std::bind(foo,_1,_2,_3)); //bind results has multiple operator() overloads
cout << f2(1,2,3) << endl;
auto f3 = make_function<int,int,int,int>(foo1); //overload1
auto f4 = make_function<float,int,int,float>(foo1); //overload2
return 0;
}
答案 2 :(得分:3)
一般情况不起作用。有一些特定情况(支持C ++ 11 lambdas但不支持C ++ 14,不支持bind
,支持非重载函数和方法,不支持函数对象),你可以在其中构建{{1那&#34;工作&#34;。您还可以编写一些有用的功能。
&#34;工作的make_function
&#34; 通常是个坏主意。
如果您不需要将其转换为make_function
,请保留原始功能对象的副本。当您已经知道要传递给它的类型以及使用返回类型执行的操作时,您只需要将其转换为std::function<?>
- 即,当您在签名周围进行类型擦除时
std::function<?>
不是&#34;功能类型的所有目的持有者&#34;。它是一个类型擦除类,用于擦除类型信息,因此您可以拥有在其上统一运行的代码。如果您要从对象中推断签名,则几乎没有理由将其存储在std::function
中。
根据您传递的函数参数的输入和输出参数类型,当您希望表现不同时,有一些很有用的情况。在这种情况下,您的签名扣除工具可能很有用:在我看来,将它绑定到std::function
将是一个坏主意,因为它将两个独立的概念(签名演绎和类型擦除)联系在一起,很少有用。
简而言之,重新考虑。
现在,我在上面提到过,有一些有用的实用程序可以称为std::function
。以下是其中两个:
make_function
但要求您列出参数。它推断了返回值。
此变体:
template<class...Args, class F>
std::function< std::result_of_t< F&(Args...) >
make_function( F&& f ) {
return std::forward<F>(f);
}
实际上并没有创建函数,但允许您调用具有多个template<class F>
struct make_function_helper {
F f;
template<class...Args>
std::result_of_t< (F&&)(Args...) >
operator()(Args&&...args)&& {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template<class...Args>
std::result_of_t< (F const&)(Args...) >
operator()(Args&&...args) const& {
return f(std::forward<Args>(args)...);
}
template<class...Args>
std::result_of_t< (F&)(Args...) >
operator()(Args&&...args) & {
return f(std::forward<Args>(args)...);
}
template<class R, class...Args, class=std::enable_if_t<
std::is_convertible<decltype( std::declval<F&>(Args...) ), R>{}
>>
operator std::function<R(Args...)>()&&{ return std::forward<F>(f); }
template<class R, class...Args, class=std::enable_if_t<
std::is_convertible<decltype( std::declval<F&>(Args...) ), R>{}
>>
operator std::function<R(Args...)>()const&{ return f; }
};
template<class F>
make_function_helper<F> make_function(F&&f) { return {std::forward<F>(f)}; }
重载的函数,并在它们之间正确选择。它也可以以完美转发的方式返回到底层std::function
。在97/100的情况下,您无法注意到此F
与返回实际make_function
的情况之间存在差异(最后一种情况是有人希望对其进行类型推断的情况来自类型的std::function
,以及完美的转发失败)
所以:
std::function
无法编译,而
int foo(std::function< int(int) >) { return 0; }
int foo(std::function< void() >) { return 1; }
int main() {
std::cout << foo( []{} ) << "\n";
}
成功。然而,即便是这个技巧也只是在int main() {
std::cout << foo( make_function([]{}) ) << "\n";
}
的设计中修补了一个漏洞,我希望这个漏洞可以归入后概念std::function
。此时,您可能只需存储原始对象。
通常,您无法为可调用对象x或函数名称x确定单个保证唯一签名。
对于可调用对象,std::function
可能有多个重载。这可以在C ++ 14中使用operator()
lambdas,使用函数对象或在C ++ 03中从[](auto x)
返回。
使用函数(或函数指针)的名称,该名称不对应于单个对象(或指针)。解析在传递给std::bind
时完成,并且经常选择正确的重载(因为std::function
采用std::function
指针,对于成员函数可能类似(我不记得) )。
使用R(*)(Args...)
这样做几乎是不可能的。
答案 3 :(得分:1)
请注意,在所有示例中,您只需删除make_function
即可获得相同的结果,或实际上更高效,因为调用std::function
通常需要进行虚拟呼叫。因此,第一个好处是在不必要时阻止使用std::function
。
通常,您使用std::function
作为某个类的成员对象(callback et similia)或作为函数的参数,无论出于何种原因都不能作为模板。在这两种情况下make_function
都没用。
struct Foo
{
std::function<int()> callback
};
Foo x; x.callback = [](){return 0;} // No need for make_function
void bar( std::function<int(int)> f );
bar( [](int i){ return i;} ); // No need for make function.
我只能想到一个案例,你可以从哪里获得一个好处:由三元运营商初始化std::function
:
auto f = val ? make_function( foo ) : make_function( bar );
可能比
更好 auto f = val ? std::function<int()>( foo ) : std::function<int()>( bar );
我认为这种情况非常罕见,因此make_function
的优势很小。
IMO的真正劣势在于假设make_function
的简单存在会鼓励经验不足的开发人员在没有必要的情况下使用std::function
,如您在代码中所示。