是否有可能专门化一个模板函数,该函数根据lambda采用的参数个数采用lambda表达式?

时间:2012-11-08 19:03:32

标签: c++ templates c++11 lambda

如果我有一个带有lambda表达式的模板化函数,我该如何对它进行特化处理,以便它可以使用带有不同数量参数的lambda表达式?以下代码有效:

template<typename Lambda>
void doSomething(Lambda callback)
{
    callback();
}

doSomething([] () { /* some code here */ } );

但如果我也想这样称呼它:

doSomething([&foo] (int x)        { /* some code here */ } );
doSomething([&foo] (int x, int y) { /* some code here */ } );

我使用什么语法来提供doSomething()的专用版本?语法如下:

template<>
void doSomething<[] (int)>([] (int) callback)
{
    callback(3);
}

在Visual Studio 2012下无法编译,我无法找到任何有效语法的引用。可能吗 ?或者,是否可以从内部告诉doSomething()提供的回调需要多少个参数,以便我可以这样做:

template<typename Lambda>
void doSomething(Lambda callback)
{
    int numArgs = ???
    switch (numArgs) {
        case 0: callback();    break;
        case 1: callback(1);   break;
        case 2: callback(1,2); break;
    }
}

2 个答案:

答案 0 :(得分:0)

lambdas有两种情况:当他们捕获时和不捕获时。

对于第一种情况,您需要提取lambda的返回类型和参数类型(来自lambda的operator()),它包含在this question中。

对于最后一种情况,你可以使用函数指针特化,因为非捕获lambdas有一个函数指针转换操作符。

关于你的doSomething()函数,不可能根据推断的参数数量直接调用仿函数,就像你在这里所做的那样:

template<typename Lambda>
void doSomething(Lambda callback)
{
    int numArgs = ???
    switch (numArgs)
    {
        case 0: callback();    break;
        case 1: callback(1);   break;
        case 2: callback(1,2); break;
    }
}

(所需功能为static if,已针对下一个C ++标准提出,请参阅n3322n3329

您需要使用辅助函数或结构,该函数或结构专用于特定的函数签名。类似的东西:

template<typename T> struct helper {};
template<typename R, typename P1>
struct helper<R(P1)>
{
    static R call(R (*lambda)(P1)) { return lambda(magic_param); }
};
template<typename R, typename P1, typename P2>
struct helper<R(P1,P2)>
{
    static R call(R(*lambda)(P1,P2)) { return lambda(magic_param_1,magic_param_2); }
};
// ...
template<typename R, typename... A>
void doSomething(R (*callback)(A...))
{
    helper<R(A...)>::call(callback);
}

这带来了一个重要问题:如何生成参数(magic_param...)?

答案 1 :(得分:0)

使用由Synxis提供的ecatmur答案的链接,我已经设法编写了我想要的东西。我重写了模板以使用typedef而不是使用using关键字的新模板别名,因此代码将适用于较旧的编译器。如果其他人想要做类似的事情我已经包含了下面的代码来说明如何做到这一点:

template<typename T> struct remove_class { };

template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { typedef R(*type)(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { typedef R(*type)(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { typedef R(*type)(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { typedef R(*type)(A...); };

template<typename T>
struct get_function_signature {
    typedef typename remove_class<decltype(&std::remove_reference<T>::type::operator())>::type type;
};

template<typename R, typename... A>
struct get_function_signature<R(A...)> { typedef R(*type)(A...); };
template<typename R, typename... A>
struct get_function_signature<R(&)(A...)> { typedef R(*type)(A...); };
template<typename R, typename... A>
struct get_function_signature<R(*)(A...)> { typedef R(*type)(A...); };

// ***************************************************************************

template<typename T> struct num_args {};

template<typename R, typename... A>
struct num_args<R(*)(A...)> { static const int value = sizeof...(A); };

template<typename C, typename R, typename... A>
struct num_args<R(C::*)(A...)> { static const int value = sizeof...(A); };

// ***************************************************************************

template<typename Lambda, int> struct callWithArgsImpl {};

template<typename Lambda>
struct callWithArgsImpl<Lambda, 1> {
    static void doIt(Lambda callback, Object* pObj, int arg) { callback(pObj); }
};

template<typename Lambda>
struct callWithArgsImpl<Lambda, 2> {
    static void doIt(Lambda callback, Object* pObj, int arg) { callback(pObj, arg); }
};

template<typename Lambda, int N>
void callWithArgs(Lambda callback, Object* pObj, int arg)
{
    callWithArgsImpl<Lambda, N>::doIt(callback, pObj, arg);
}

// ***************************************************************************

template<typename Lambda>
void doSomething(int x, Lambda callback)
{
    // some code here which gets a pointer to an Object (pObj) and an
    // extra piece of information about where the object came from (arg1)

    const int numArgs = num_args<typename get_function_signature<Lambda>::type>::value;

    callWithArgs<Lambda, numArgs>(callback, pObj, arg1);
}

允许代码调用doSomething()函数传入lamba表达式,该表达式总是需要Object *参数,但如果不需要则可以省略第二个参数。所以以下两行都有效:

doSomething(5, [] (Object* pObj)           { printf("lambda called with no arg1\n"); } );
doSomething(2, [] (Object* pObj, int arg1) { printf("lambda called with arg1=%d\n", arg); } );

它有效,但我不禁感到它尽管语言规范而不是因为它而起作用。使用lambda表达式作为回调函数似乎是一件很自然的事情。我绝对可以看到我想要指定具有不同签名的回调的情况。这应该比这更容易......