推断 C++ 中 lambda 的模板类型
Infer template type of lambda in C++
我必须遵循类:
template<typename T>
class SafeCallback
{
public:
typedef std::function<T> FunctionType;
SafeCallback(std::shared_ptr<bool> is_valid, FunctionType callback)
: is_valid_(is_valid), callback_(callback)
{
}
template <class ...Arg>
void operator()(Arg&&... parameters)
{
if ((*is_valid_) == true)
{
callback_(std::forward<Arg>(parameters)...);
}
}
private:
std::shared_ptr<bool> is_valid_;
FunctionType callback_;
};
要使用 lambda 作为回调构造这样的对象,我可以做这样的事情:
SafeCallback<void(int)>(guard, [] (int value) { /* Do sthing */ });
但我想C++应该有一种方法可以使用工厂方法推断SafeCallback
的类型参数。如果我创建这样的方法:
template<typename T>
SafeCallback<T> makeSafe(std::shared_ptr<bool> is_valid, std::function<T> callback)
{
return SafeCallback<T>(is_valid, callback);
}
但这不适用于 lambdas,只有当我传递std::function
.有什么想法吗?
不要不必要地键入擦除。 相反,存储原始F
,并提供到兼容的其他SafeCallback
的转换:
template<typename F>
class SafeCallback {
public:
// helper alias. `compatible<O>` is `void` iff O is compatible
// with initializing an `F`. If not, it is a SFINAE failure.
template<class O>
using compatible=std::enable_if_t<std::is_convertible<O,F>::value>;
// Construct from an arbitrary `T` with perfect forwarding:
template<class T, class=compatible<T>>
SafeCallback(std::shared_ptr<bool> is_valid, T&& callback):
is_valid_(is_valid),
callback_(std::forward<T>(callback))
{}
// invoke, non-`const` version:
template <class ...Arg>
void operator()(Arg&&... parameters) {
if ((*is_valid_) == true) {
callback_(std::forward<Arg>(parameters)...);
}
}
// invoke, `const` version:
template <class ...Arg>
void operator()(Arg&&... parameters)const {
if ((*is_valid_) == true) {
callback_(std::forward<Arg>(parameters)...);
}
}
// explicit copy and move ctors:
SafeCallback(SafeCallback const& o)=default;
// in MSVC you'll have to write this one in 2 lines:
SafeCallback(SafeCallback && o)=default;
// and write `operator=(&&)` in MSVC as well.
// copy from a compatible SafeCallback<O>:
template<class O, class=compatible<O const&>>
SafeCallback(SafeCallback<O> const& o):
is_valid_(o.is_valid_),
callback_(o.callback_)
{}
// copy from a compatible SafeCallback<O> rvalue:
template<class O, class=compatible<O>>
SafeCallback(SafeCallback<O> && o):
is_valid_(std::move(o.is_valid_)),
callback_(std::move(o.callback_))
{}
// efficient operator= optional: conversion will work if you
// don't bother I think?
private:
std::shared_ptr<bool> is_valid_;
F callback_;
// ensure our siblings can access our privates:
template<class O>
friend class SafeCallback;
};
现在我们得到:
// the F_v is just a way to introduce a new derived type
// for storage -- DRY optimization:
template<class F, class F_v=std::decay_t<F>>
SafeCallback<F_v>
makeSafe(std::shared_ptr<bool> is_valid, F&& callback) {
return SafeCallback<F_v>(is_valid, std::forward<F>(callback));
}
活生生的例子。
如果要将SafeCallback
存储在非类型推断上下文中,则需要手动键入类型。 如果您处于类型推断的上下文中,为什么要擦除类型信息? 这需要提高效率。
另外,以上支持变分和auto
λ。 到处都是管道。
请注意,SFINAE 支持在 MSVC 中可能有效,也可能无效,但这并不重要(class=compatible
条款)。 此外,我对一些std::
特征使用了_t
别名 - 如果您的 std 库缺少它们,请将blah_t<?>
替换为 typename blah<?>::type
。 接下来,请注意,转换为std::function
的测试将不起作用 SFINAE,因为std::function
有一些过于贪婪的蹩脚的 ctor。 我希望该标准的未来迭代将解决这个问题。
std::function
不是"用于存储各种可调用对象的通用容器"——它是一种类型擦除机制,可以擦除几乎所有内容,除了它是可调用的事实。 当您想要抛出有关类型的信息时,可以键入 era。
根据要擦除的传入类型的属性键入擦除是非常、非常罕见的好主意。 如果希望输出类型依赖于输入类型,通常使用输入类型比提取其中的一部分更好。 如果您希望它是独立的,则意味着代码是独立于输入类型编写的,并且您希望匹配兼容性,而不是提取属性并擦除除提取属性之外的所有属性。
最后,我发现依赖于std::weak_ptr<void>
的安全回调是最好的。 源对象提供了一个std::shared_ptr<?>
(可能是指向this
的指针,也可能是指向this
持有的令牌),它保证只要类本身就会存在。 在SafeCallback
中,您if (auto _ = is_valid_.lock())
查看目标是否还活着。
仍然存在竞争条件或重入危险,但它处理了很多问题。
lambda与std::function<T>
不完全匹配,因此无法推断T
。
编辑:(删除以前的错误答案)
您可以使用 decltype
获取 lambda 的operator()
类型,并使用帮助程序重建类型:
template <typename T> struct helper : helper<decltype(&T::operator())> {};
template <typename Ret, typename C, typename ... Args>
struct helper<Ret (C::*)(Args...) const>
{
using type = Ret(Args...);
};
template<typename T>
SafeCallback<typename helper<T>::type>
makeSafe(std::shared_ptr<bool> is_valid, T callback)
{
return SafeCallback<typename helper<T>::type>(is_valid, callback);
}
现场示例
这要求函子(您的 lambda)没有多个重载operator()
。
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 在 lambda 捕获中声明的变量的类型推导
- C++ 模板类型的静态 lambda 成员的构造
- Clang 工具,用于提取给定 lambda 类型的 lambda 主体
- 如果 lambda 没有指定的类型,std::function 如何接受 lambda?
- Lambda可以用作非类型模板参数吗
- 如何确定捕获不可复制参数的 lambda 的类型?
- 如何制作可以接受任何类型的参数的 std::函数和 lambda
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 运算符中的不同类型? 具有无捕获,相同的签名,lambda
- 通过参数传递 lambda(无函数类型模板)
- 有没有办法根据 lambda 参数返回类型部分专用化我的模板化函数?
- 如何推导lambda的返回类型
- 尽管显式声明了返回类型,但对lambda的调用是不明确的
- 是否创建具有不同返回类型的lambda
- 防止 lambda 的返回类型扣除
- 在 lambda 函数 g++-4.8 中调用继承的受保护子类型
- 在 lambda 中从引用类型捕获的值的类型,不使用通用捕获
- 我可以让返回类型自动处理具有相同签名但捕获不同内容的 lambda 吗?
- 复制分配C++相同类型的 lambda