1

如果我有一个采用 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;
    }
}
4

2 回答 2

0

lambda 有两种情况:何时捕获,何时不捕获。

对于第一种情况,您需要提取 lambda 的返回类型和参数类型(来自operator()lambda 的),这在这个问题中有所涉及。

对于最后一种情况,您可以使用函数指针特化,因为非捕获 lambda 具有函数指针转换运算符。

关于您的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...) ?

于 2012-11-08T23:25:36.507 回答
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() 函数,传入一个始终需要 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 表达式作为回调函数似乎是一件很自然的事情。而且我绝对可以看到我想要指定具有不同签名的回调的情况。这应该比这更容易做到......

于 2012-11-18T22:44:13.057 回答