【问题标题】:passing lambda to void specified template fails将 lambda 传递给 void 指定模板失败
【发布时间】:2019-05-04 16:25:06
【问题描述】:

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

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

如果我使用 int 类型调用该函数,一切正常,但是,如果我使用 void 类型的 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

有没有办法解决这个问题?有人将如何指定模板以使两个调用都起作用?

【问题讨论】:

  • void 不是可以传递给函数的参数类型。 std::function&lt;void(T)&gt; 只接受一个 T 类型的参数,而你的第一个 lambda 只接受零个参数
  • 这里是相关提案P0146。如果您有时间花时间阅读,我想会很有趣:) 不过,不知道它的当前状态是什么。

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


【解决方案1】:

当然,括号中的 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 的特化需要一个空函数对象)。

【讨论】:

  • Kinda 必须以任何方式专门化该成员,因为不能天真地编写主要成员以与 void 一起使用(它应该如何产生“空值”?)
  • 我不确定,我会在哪里添加这一行。如果我把它放在模板函数下面,它就行不通了,我也试图把它放到一个 .cpp 文件中,但没有成功。你能详细说明把这个放在哪里吗?
【解决方案2】:

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

// 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;
     });

【讨论】:

  • 这个解决方案解决了我的语法错误,非常棒。但是,我的项目显然比我给出的示例更复杂,最后这导致了很多代码重复,这对我来说不是很理想。我的直觉有一种奇怪的感觉,这是唯一的解决方案,所以谢谢很多。
【解决方案3】:

来不及玩了?

我提出了另一种基于自定义类型特征(专门针对void)的解决方案,给定T 类型,定义正确的std::function type;我的意思是

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

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

这样你的ExecuteFunction()就变成了

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

如果你想稍微简化getFuncType的使用,你可以添加一个using帮助器来提取type

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

所以ExecuteFunction()可以简化如下

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

【讨论】:

  • 太棒了,正是我需要的。
猜你喜欢
  • 2017-03-07
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-25
  • 2015-09-10
相关资源
最近更新 更多