推断 C++ 中 lambda 的模板类型

Infer template type of lambda in C++

本文关键字:类型 lambda C++ 推断      更新时间:2023-10-16

我必须遵循类:

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()