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

mat*_*jda 2 c++ lambda templates generic-programming c++11

我必须上课:

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_;
};
Run Code Online (Sandbox Code Playgroud)

要使用lambda作为回调来构造这样的对象,我可以这样做:

SafeCallback<void(int)>(guard, [] (int value) { /* Do sthing */ });
Run Code Online (Sandbox Code Playgroud)

但我想,C++应该有一种方法来推断SafeCallback使用工厂方法的类型参数.如果我创建这样的方法:

template<typename T>
SafeCallback<T> makeSafe(std::shared_ptr<bool> is_valid, std::function<T> callback)
{
    return SafeCallback<T>(is_valid, callback);
}
Run Code Online (Sandbox Code Playgroud)

但这不适用于lambdas,只有我通过了std::function.有任何想法吗?

Yak*_*ont 5

不要不必要地键入擦除.相反,存储raw F,并提供转换为兼容的其他SafeCallbacks:

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;
};
Run Code Online (Sandbox Code Playgroud)

现在我们得到:

// 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));
}
Run Code Online (Sandbox Code Playgroud)

实例.

如果要将您存储SafeCallback在非类型推导的上下文中,则需要手动键入类型.如果您处于类型推导的上下文中,为什么要删除类型信息?这会带来效率.

另外,上面支持variardic和autolambdas.到处都是管道.

请注意,SFINAE支持在MSVC中可能有效,也可能无效,但并不重要(class=compatible条款).另外,我用_t别名几个std::特点-更换blah_t<?>typename blah<?>::type,如果你的性病库缺少他们.接下来,请注意转换为的测试对std::functionSFINAE不起作用,因为std::function有一些过于贪婪的蹩脚的ctors.我希望未来的标准迭代能够解决这个问题.

std::function不是一个"存放各种可调用的通用集装箱" -这是一种擦除机制消除几乎所有的东西,除了一个事实,即它是可调用.如果要丢弃有关类型的信息,请键入erase.

根据要擦除的传入类型的属性键入erase是非常非常好的主意.如果希望输出类型取决于输入类型,通常使用输入类型比提取输出类型更好.如果您希望它是独立的,则意味着代码的编写与输入类型无关,并且您希望匹配兼容性,而不是提取属性并擦除所有提取的属性.

最后,我发现依赖于std::weak_ptr<void>最佳的安全回调.源对象提供了一个std::shared_ptr<?>(可能是一个指针this,可能是一个this持有的令牌),它保证只要类本身就可以存活.在SafeCallbackif (auto _ = is_valid_.lock())看看目标是否还活着.

仍有竞争条件或重入危险,但它处理了很多问题.