将lambda传递给无效的指定模板失败

时间:2018-12-03 11:17:28

标签: c++ c++11 templates lambda std-function

我尽可能地简化了问题,所以这里是有问题的函数:

class Test
{
public:
    template<class T>
    void ExecuteFunction(std::function<void(T)> f)
    {
    }
};

如果我使用int-typing调用该函数,则一切正常,但是,如果我使用空类型的lambda调用它,它将不再编译。

Test test;

test.ExecuteFunction<void>(    // doesn't compile
    [](void)->void
{
    int i = 5;
});

test.ExecuteFunction<int>(    // this compiles
    [](int)->void
{
    int i = 5;
});

编译器错误:

Error   C2672   'Test::ExecuteFunction': no matching overloaded function found  
Error   C2770   invalid explicit template argument(s) for 'void Test::ExecuteFunction(std::function<void(P)>)'  
Error (active)      no instance of function template "Test::ExecuteFunction" matches the argument list

有没有解决的办法?有人将如何指定模板,以便两个调用都能正常工作?

3 个答案:

答案 0 :(得分:4)

当然,括号内的void只是老式的C型糖。您必须专门设计模板:

template<> void Test::ExecuteFunction<void>(std::function<void()> f) {}

如果无法编译,可以使用帮助程序模板封装类型选择:

#include <iostream>
#include <functional>

template<class T> struct callable {
    using type = std::function<void(T)>;
};
template<class T> using callable_t =
    typename callable<T>::type;
template<> struct callable<void> {
    using type = std::function<void()>;
};

class Test
{
public:
    template<class T>
    void ExecuteFunction(callable_t<T> f) {}
};

int main() {
    Test test;

    test.ExecuteFunction<void>(    // does compile
                    [](void)->void {});

    test.ExecuteFunction<int>(    // this compiles
                    [](int)->void {});
}

但是请注意,这种方式还必须对传递的参数做一些处理(在您的示例中,通用案例的参数是一元的,但是void的特殊化要求使用空函数对象)。

答案 1 :(得分:3)

您可以像这样向类添加重载:

// as before:
template<class T>
void ExecuteFunction(std::function<void(T)> f) {}

// new overload (not a template):
void ExecuteFunction(std::function<void()> f) {}

由于您无论如何都不能使用类型推导,因此现在可以通过不指定任何模板参数来显式调用此函数。

Test test;

test.ExecuteFunction(
     [](void)->void
     {
     int i = 5;
     });

答案 2 :(得分:2)

Is too late to play?

I propose another solution based on a custom type trait (with a specialization for void) that, given a T type, define the correct std::function type; i mean

template <typename T>
struct getFuncType
 { using type = std::function<void(T)>; };

template <>
struct getFuncType<void>
 { using type = std::function<void()>; };

This way your ExecuteFunction() simply become

template <typename T>
void ExecuteFunction (typename getFuncType<T>::type f)
{
}

If you want simplify a little the use of getFuncType, you can add a using helper to extract the type

template <typename T>
using getFuncType_t = typename getFuncType<T>::type;

so the ExecuteFunction() can be simplified as follows

template <typename T>
void ExecuteFunction (getFuncType_t<T> f)
{
}