如何在不指定模板参数的情况下将 lambda 与模板参数匹配
How to match lambda to template parameter without specifying template params
我正在编写一个消息接收库,并希望编写一个简单的lambda,以便在给定端点上收到消息时调用。
当我尝试一个简单的 lambda 时,它与std::function
模板不匹配,因为 lambda 不是确切的类型,这是有道理的。
#include <iostream>
#include <unistd.h>
#include <functional>
#include <memory>
const std::string endpoint1 = "ipc:///tmp/endpoint1.ipc";
const std::string endpoint2 = "ipc:///tmp/endpoint2.ipc";
class ISubscriber {
public:
virtual ~ISubscriber() {};
};
template <typename T>
class Subscriber : public ISubscriber
{
public:
Subscriber(const std::string & endpoint, std::function<void (const T&)> callback);
};
class Context
{
public:
template <typename T>
void addSubscriberListener(const std::string & endpoint, std::function<void(const T &)> callback)
{
// add to subscribers list
}
private:
std::vector<std::unique_ptr<ISubscriber>> _subscribers;
// All the other goo to make messaging work
};
class Type1 {};
class Type2 {};
void test()
{
Context context;
#if 1 // Desired syntax
context.addSubscriberListener(endpoint1, [] (const Type1 & t) {
});
context.addSubscriberListener(endpoint2, [] (const Type2 & t) {
});
#else // Undesired syntax
context.addSubscriberListener(endpoint1, std::function<void(const Type1 &)>([] (const Type1 & t) {
}));
context.addSubscriberListener(endpoint2, std::function<void(const Type2 &)>([] (const Type2 & t) {
}));
#endif
}
所需的语法给了我
Test.cpp: In function ‘void test()’:
Test.cpp:43:10: error: no matching function for call to ‘Context::addSubscriberListener(const string&, test()::<lambda(const Type1&)>)’
});
^
Test.cpp:25:11: note: candidate: ‘template<class T> void Context::addSubscriberListener(const string&, std::function<void(const T&)>)’
void addSubscriberListener(const std::string & endpoint, std::function<void(const T &)> callback)
^~~~~~~~~~~~~~~~~~~~~
Test.cpp:25:11: note: template argument deduction/substitution failed:
Test.cpp:43:10: note: ‘test()::<lambda(const Type1&)>’ is not derived from ‘std::function<void(const T&)>’
});
^
Test.cpp:45:7: error: no matching function for call to ‘Context::addSubscriberListener(const string&, test()::<lambda(const Type2&)>)’
});
^
Test.cpp:25:11: note: candidate: ‘template<class T> void Context::addSubscriberListener(const string&, std::function<void(const T&)>)’
void addSubscriberListener(const std::string & endpoint, std::function<void(const T &)> callback)
^~~~~~~~~~~~~~~~~~~~~
Test.cpp:25:11: note: template argument deduction/substitution failed:
Test.cpp:45:7: note: ‘test()::<lambda(const Type2&)>’ is not derived from ‘std::function<void(const T&)>’
});
对我来说,我是否需要额外的管道,或者管道应该是什么才能使其工作,这对我来说并不明显。
我有哪些选项可以获得所需的语法?
但是你真的需要那个addSubscriberListener()
收到std::function
?
简单地接受通用类型F
(用于"功能"(怎么样?
template <typename F>
void addSubscriberListener (const std::string & endpoint, F callback)
{
// add to subscribers list
}
通过这种方式,您可以解决您遇到的先有鸡还是先有蛋的问题:lambda 可以转换为std::function
但不是std::function
,因此编译器必须知道T
类型才能将 lambda 转换为std::function
并从中扣除T
类型。
无论如何。。。如果您需要维护std::function
...不完全是想要的语法,但是...您可以显式T
类型
// --------------------------VVVVVVV
context.addSubscriberListener<Type1>(endpoint1, [] (const Type1 & t) {
});
context.addSubscriberListener<Type2>(endpoint2, [] (const Type2 & t) {
});
// --------------------------^^^^^^^
这是打破先有鸡还是先有蛋问题的另一种方法:你显式T
类型,所以编译器知道lambda必须用来初始化std::function<void(Type1 const &)>
(拳头调用(和std::function<void(Type2 const &)>
(第二次调用调用(。
--编辑--
OP精确地表明
在某些时候,我需要声明一种要解压缩的 lambda 参数类型,然后将其传递给 lambda...
OP还精确地可以使用C++17,所以...使用std::function
演绎指南从F
推导T
呢?
鉴于您的callback
可调用对象仅接收一个参数,您可以编写(抱歉:代码未测试(
template <typename F>
void addSubscriberListener (const std::string & endpoint, F callback)
{
using T = std::remove_cv_t<
std::remove_reference_t<
typename decltype(std::function{callback})::argument_type;
// add to subscribers list
}
不幸的是,仅当std::function
只接收一个参数时,std::function::argument_type
才可用,此外,在 C++17 中已弃用并从 C++20 中删除。
所以也许你可以写一个自定义的类型特征,如下所示
template <typename>
struct getTypeFirstArg;
template <typename R, typename T1, typename ... Ts>
struct getTypeFirstArg<std::function<R(T1, Ts...)>>
{ using type = T1; };
并提取T
using T = std::remove_cv_t<
std::remove_reference_t<
typename getTypeFirstArg<decltype(std::function{f})>::type>>;
以下是完整的编译示例
#include <functional>
#include <type_traits>
template <typename>
struct getTypeFirstArg;
template <typename R, typename T1, typename ... Ts>
struct getTypeFirstArg<std::function<R(T1, Ts...)>>
{ using type = T1; };
template <typename F>
void foo (F f)
{
using T = std::remove_cv_t<
std::remove_reference_t<
typename getTypeFirstArg<decltype(std::function{f})>::type>>;
static_assert( std::is_same_v<T, int> );
}
int main ()
{
foo([&](int const &){});
}
另请参阅纪尧姆·拉西科特(Guillaume Racicot(的答案,该答案也应该适用于C++11和C++14。
您应该跳过std::function
并直接使用 lambda:
template <typename F>
void addSubscriberListener(std::string const& endpoint, F callback)
{
// add to subscribers list
}
这使得类型推断可用于 lambda 类型
在某些时候,我需要声明一种要解压缩的 lambda 参数类型,然后将其传递给 lambda。
因此,如果我了解您需要考虑 lambda 的参数类型?
这可以使用类似的模式来完成 boost::callable:
template<typename>
struct extract_arg_types {};
template<typename R, typename C, typename Arg>
struct extract_arg_types<R(C::*)(Arg) const> {
using arg_type = Arg;
};
然后你可以在你的类中使用它:
template <typename F>
void addSubscriberListener(std::string const& endpoint, F callback)
{
using T = typename extract_arg_types<decltype(&T::operator())>::arg_type;
// add to subscribers list
}
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 如何将lambda作为模板类的成员函数参数
- 使用自动推导的 lambda 参数作为常量表达式
- 如果模板没有可变参数,则 Lambda 被推导出为 std::function
- 将有状态的 lambda 传递到 C 样式函数中,而无需上下文参数
- 引用捕获和在 lambda 中通过引用发送参数有什么区别 (C++)
- 将__device__ lambda 作为参数传递给 __global__ 函数
- 将 lambda 函数作为参数传递C++
- 如何将 lambda 函数作为参数发送到另一个函数
- Lambda可以用作非类型模板参数吗
- 将参数传递给泛型 lambda 时复制构造函数不正确
- 如何确定捕获不可复制参数的 lambda 的类型?
- 省略C++可变参数 lambda 中的"auto"关键字?
- 如何访问可变参数 lambda 函数参数
- 可变参数 lambda 捕获的解决方法
- 传递给多个参数 lambda 表达式的参数
- 如何编写丢弃其参数的通用可变参数 lambda
- 使用c++参数lambda函数
- 将函数模板"pass"为泛型可变参数 lambda 返回语句是好方法吗?
- c++模板非类型参数lambda函数