Wal*_*ter 15 c++ function c++11
std::function<>几乎任何可调用的东西都是一个有用的包装器,包括自由函数,lambda,仿函数,成员函数,结果std::bind.但是,在创建a时std::function<>,必须明确指定函数签名(取自此处)
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();
Run Code Online (Sandbox Code Playgroud)
即使签名可以从指定的对象推断出来.似乎避免这种情况的一种自然方式(在重度模板化的代码中应该非常方便)是一个重载的函数模板make_function(在精神上与std::make_pairor 相似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());
Run Code Online (Sandbox Code Playgroud)
另一个可能的用例是获取任何类型的可调用对象的返回类型
decltype(make_function(function_object))::return_type;
Run Code Online (Sandbox Code Playgroud)
避免Piotr S.回答这个问题时的特质魔力.
所以,我的问题是:为什么标准不提供此功能?可以make_function没有编译器魔法来实现?还是需要编译魔术?(即便如此,第一个问题仍然存在.)
Dra*_*rax 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());
}
Run Code Online (Sandbox Code Playgroud)
因为func这里的类型是什么?
这种歧义适用于所有函子对象(包括bind返回值和lambda),这些对象make_function只能在函数指针上使用.
正如此处和其他地方所评论的那样,存在可能使类型推理混淆的模糊性问题.可能这些极端情况阻止了std::make_function被采用,因为它无法解决模糊,重载或与C++自动类型转换很好地协作.反对它的另一个论点,我看到很多是std::function有开销(在类型擦除),并且许多人反对std::function在除了存储可调用之外的任何东西上使用.
但是,对于非模糊的情况,可以编写一个make_function用于处理类型推理的lambdas和其他callables,这可以避免在没有歧义时重复函数类型签名.一种方法(取自我的相关问题)如下:
#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;
}
Run Code Online (Sandbox Code Playgroud)