【问题标题】:c++0x: overloading on lambda arityc++0x:在 lambda arity 上重载
【发布时间】:2010-11-13 00:32:33
【问题描述】:

我正在尝试创建一个函数,该函数可以使用带有 0、1 或 2 个参数的 lambda 调用。由于我需要代码在 g++ 4.5 和 vs2010 上工作(它不支持可变参数模板或 lambda 转换为函数指针),所以我想出的唯一想法是根据 arity 选择要调用的实现。以下是我对这应该如何看待的非工作猜测。有什么方法可以修复我的代码,还是有更好的方法来解决这个问题?

#include <iostream>
#include <functional>
using namespace std;

template <class Func> struct arity;

template <class Func>
struct arity<Func()>{ static const int val = 0; };

template <class Func, class Arg1>
struct arity<Func(Arg1)>{ static const int val = 1; };

template <class Func, class Arg1, class Arg2>
struct arity<Func(Arg1,Arg2)>{ static const int val = 2; };

template<class F>
void bar(F f)
{
    cout << arity<F>::val << endl;
}

int main()
{
    bar([]{cout << "test" << endl;});
}

【问题讨论】:

  • 你能展示一个调用 lambda 函数的代码示例吗?

标签: c++ lambda c++11


【解决方案1】:

lambda 函数是具有单个函数调用运算符的类类型。因此,您可以通过获取其地址并使用重载解析来选择要调用的函数来检测该函数调用运算符的数量:

#include <iostream>

template<typename F,typename R>
void do_stuff(F& f,R (F::*mf)() const)
{
    (f.*mf)();
}

template<typename F,typename R,typename A1>
void do_stuff(F& f,R (F::*mf)(A1) const)
{
    (f.*mf)(99);
}

template<typename F,typename R,typename A1,typename A2>
void do_stuff(F& f,R (F::*mf)(A1,A2) const)
{
    (f.*mf)(42,123);
}

template<typename F>
void do_stuff(F f)
{
    do_stuff(f,&F::operator());
}

int main()
{
    do_stuff([]{std::cout<<"no args"<<std::endl;});
    do_stuff([](int a1){std::cout<<"1 args="<<a1<<std::endl;});
    do_stuff([](int a1,int a2){std::cout<<"2 args="<<a1<<","<<a2<<std::endl;});
}

但请注意:这不适用于函数类型,或具有多个函数调用运算符的类类型,或非const 函数调用运算符。

【讨论】:

  • 非常酷,但是您不需要实际调用该函数(因为它限制了参数类型)并且您希望从do_stuff 返回参数的数量(可能是最好命名为arity)。
  • 有了 C++11 和可变参数模板,我们现在可以做得更好。
【解决方案2】:

我认为以下内容会起作用,但事实并非如此,我发布它有两个原因。

  1. 如果他们有相同的想法,可以为他们节省时间
  2. 如果有人知道为什么这不起作用,我不能 100% 确定我理解(尽管我有怀疑)

代码如下:

#include <iostream>
#include <functional>

template <typename Ret>
unsigned arity(std::function<Ret()>) { return 0; }

template <typename Ret, typename A1>
unsigned arity(std::function<Ret(A1)>) { return 1; }

template <typename Ret, typename A1, typename A2>
unsigned arity(std::function<Ret(A1, A2)>) { return 2; }

// rinse and repeat 

int main() 
{
    std::function<void(int)>  f = [](int i) { }; // this binds fine

    //  Error: no matching function for call to 'arity(main()::<lambda(int)>)'
    std::cout << arity([](int i) { }); 
} 

【讨论】:

  • std::function 知道如何包装 lambda,但 you 必须直接提供正确的 <...> 签名。例如:给定“template void foo(vector);”,你不能给 foo 传递一个初始化列表!编译器知道如何处理 vector::vector(initializer_list),但它必须首先知道 T 才能做到这一点。编译器不能从 initializer_list 开始,看到一个向量,然后神奇地选择 T=X,这样事情就会匹配。落后了。
【解决方案3】:

编译时获取函数或函数对象的arity的方法,包括lambda的arity:

int main (int argc, char ** argv) {
    auto f0 = []() {};
    auto f1 = [](int) {};
    auto f2 = [](int, void *) {};

    std::cout << Arity<decltype(f0)>::value << std::endl; // 0
    std::cout << Arity<decltype(f1)>::value << std::endl; // 1
    std::cout << Arity<decltype(f2)>::value << std::endl; // 2

    std::cout << Arity<decltype(main)>::value << std::endl; // 2
}

template <typename Func>
class Arity {
private:
    struct Any {
        template <typename T>
        operator T ();
    };

    template <typename T>
    struct Id {
        typedef T type;
    };

    template <size_t N>
    struct Size {
        enum { value = N };
    };

    template <typename F>
    static Size<0> match (
        F f,
        decltype(f()) * = nullptr);

    template <typename F>
    static Size<1> match (
        F f,
        decltype(f(Any())) * = nullptr,
        decltype(f(Any())) * = nullptr);

    template <typename F>
    static Size<2> match (
        F f,
        decltype(f(Any(), Any())) * = nullptr,
        decltype(f(Any(), Any())) * = nullptr,
        decltype(f(Any(), Any())) * = nullptr);

public:
    enum { value = Id<decltype(match(static_cast<Func>(Any())))>::type::value };
};

【讨论】:

    【解决方案4】:

    这种方式有效:

    template<typename F>
    auto call(F f) -> decltype(f(1))
    {
        return f(1);
    }
    
    template<typename F>
    auto call(F f, void * fake = 0) -> decltype(f(2,3))
    {
        return f(2,3);
    }
    
    template<typename F>
    auto call(F f, void * fake = 0, void * fake2 = 0) -> decltype(f(4,5,6))
    {
        return f(4,5,6);
    }
    
    int main()
    {
        auto x1 = call([](int a){ return a*10; });
        auto x2 = call([](int a, int b){ return a*b; });
        auto x3 = call([](int a, int b, int c){ return a*b*c; });
        // x1 == 1*10
        // x2 == 2*3
        // x3 == 4*5*6
    }
    

    它适用于所有可调用类型(lambda、仿函数等)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 2011-07-25
      • 1970-01-01
      • 2010-12-29
      • 1970-01-01
      • 2014-10-31
      相关资源
      最近更新 更多