带有默认返回值的C++弱函子

C++ weak functor with default return value

本文关键字:C++ 默认 返回值      更新时间:2023-10-16

在这个社区的帮助下,我有了以下模板方法。。。

// void return
template<typename R, typename = typename std::enable_if<std::is_void<R>::value, R>::type,
         typename F, typename T, typename... A>
static std::function<R()> Weak(F func, const std::shared_ptr<T>&& obj, A&&... args)
{
    return std::bind(Wrapper<R>(), std::weak_ptr<T>(obj),
                     std::function<R()>(std::bind(func, obj.get(), args...)));
}
// non void return
template<typename R, R D = R(), typename = typename std::enable_if<std::is_void<R>::value == false, R>::type,
         typename F, typename T, typename... A>
static std::function<R()> Weak(F func, const std::shared_ptr<T>&& obj, A&&... args)
{
    return std::bind(Wrapper<R>(), std::weak_ptr<T>(obj),
                     std::function<R()>(std::bind(func, obj.get(), args...)), D);
}

其中,Wrapper是一个模板类,用于测试弱指针,并在指定的默认值过期时返回该值。用法示例如下。。。

(a) Bind::Weak<void>(func, obj, args...)
(b) Bind::Weak<bool>(func, obj, args...)        // default bool is "false"
(c) Bind::Weak<bool, true>(func, obj, args...)
(b) Bind::Weak<int>(func, obj, args...)         // default int is "0"
(c) Bind::Weak<int, 42>(func, obj, args...)

是否可以支持以下用法?

(a) Bind::Weak<void>(func, obj, args...)
(b) Bind::Weak<true>(func, obj, args...)
(c) Bind::Weak<42>(func, obj, args...)

[编辑]下面Oktalist的回复给了我以下想法。。。

// weak from shared - void return
template<typename F, typename O, typename... A>
static std::function<void()> Weak(F func, const std::shared_ptr<O>&& obj, A&&... args)
{
    return std::bind(Wrapper<void>(), std::weak_ptr<O>(obj), std::function<void()>(std::bind(func, obj.get(), std::forward<A>(args)...)));
}
// weak from shared - non-void return
template<typename F, typename R = typename std::enable_if<std::is_void<F>::value == false, F>::type, typename O, typename... A>
static std::function<R()> Weak(R&& val, F func, const std::shared_ptr<O>&& obj, A&&... args)
{
    return std::bind(Wrapper<R>(), std::weak_ptr<O>(obj), std::function<R()>(std::bind(func, obj.get(), std::forward<A>(args)...)), val);
}

它给出了这种用法。。。

(a) Bind::Weak(func, obj, args...)
(b) Bind::Weak(true, func, obj, args...)
(c) Bind::Weak(42, func, obj, args...)

我能想到的唯一可能帮助您解决这个问题的方法是为您可能支持的每种非类型模板参数重载您的非void版本的弱。在您的示例中,这是一个bool和一个int。

这不是你的代码,因为它被简化了,但希望能传达这样的想法:

// accepts a type. using enable_if you'd do something like this
// to catch all the non-void types that don't have a default value.
template <class R, ...enable_if to only deal with non-void... >
R Get( R r )
{
  return r;
}
// this for if the user wants to put an actual value as a parameter
// to be the default rather than just 'int'
template <int R>
decltype(R) Get( decltype(R) r = R )
{
  return r;
}

// ... and do the previous one for each specific type which can have a default

基本上,问题是像"42"answers"false"这样的特定值不会绑定到"typename"。因此,您需要函数的专用模板来接受这些值,然后您可以在需要使用decltype((时获得该值的类型。这将消除有时需要一个参数(即:Get<int>(而有时需要两个参数(例如:Get&llt;int,42>以指定默认值(的需要。

对于您的示例来说,另一个似乎可以接受但在"现实世界"中可能不可接受的限制是对模板参数本身的限制。你将无法执行弱<foo>,其中foo是一个类的实例。

我在同一个问题上花了一些时间,并提出了以下问题:

#include <functional>
#include <memory>
template <typename F, typename T>
struct Invoker {
  typedef std::weak_ptr<T> P;
  Invoker(F func, P ptr)
    : func_(func),
      ptr_(ptr) {
  }
  template <typename... Args>
  typename std::result_of<F(Args...)>::type operator()(Args&&... args) {
    typedef typename std::result_of<F(Args...)>::type R;
    if (ptr_.lock())
      return func_(std::forward<Args>(args)...);
    return R();
  }
private:
  F func_;
  P ptr_;
};
template <typename F, typename T, typename... Args>
struct _Bind_helper {
  typedef Invoker<decltype( std::bind(std::declval<F>(), std::declval<Args>()...) ), T> InvokerType;
};
template <typename F, typename T, typename... Args>
typename _Bind_helper<F, T, Args...>::InvokerType
weak_bind(std::weak_ptr<T> ptr, F func, Args&&... args) {
  typedef typename _Bind_helper<F, T, Args...>::InvokerType R;
  return R(std::bind(std::forward<F>(func), std::forward<Args>(args)...), ptr);
}

默认结果值有明显的限制,但它似乎在大多数情况下都有效:(

是的,您可以使用函数特性来推导可调用类型F的返回类型。

从模板参数列表中删除typename R,将typename F移动到模板参数列表的开头,并用typename function_traits<F>::return_type替换所有出现的R。我们可以使用template using声明来帮助我们:

template <typename F>
using Ret = typename function_traits<F>::return_type;
template <typename T>
using EnableIfVoid = typename std::enable_if<std::is_void<T>::value, T>::type;
template <typename T>
using EnableIfNotVoid = typename std::enable_if<! std::is_void<T>::value, T>::type;
// void return
template<typename F, typename = EnableIfVoid<Ret<F>>, typename T, typename... A>
static std::function<void()> Weak(F func, const std::shared_ptr<T>& obj, A&&... args)
{
    return std::bind(Wrapper<void>(), std::weak_ptr<T>(obj),
                     std::function<void()>(std::bind(func, obj.get(),
                                                     std::forward<Args>(args)...)));
}
// non void return with explicit default
template<typename F, typename = EnableIfNotVoid<Ret<F>>, typename T, typename... A>
static std::function<Ret<F>()> Weak(Ret<F> d, F func, const std::shared_ptr<T>& obj, A&&... args)
{
    return std::bind(Wrapper<Ret<F>>(), std::weak_ptr<T>(obj),
                     std::function<Ret<F>()>(std::bind(func, obj.get(),
                                                       std::forward<Args>(args)...)), d);
}
// non void return with implicit default
template<typename F, typename = EnableIfNotVoid<Ret<F>>, typename T, typename... A>
static std::function<Ret<F>()> Weak(F func, const std::shared_ptr<T>& obj, A&&... args)
{
    return Weak(Ret<F>(), func, obj, std::forward<Args>(args)...);
}

我必须将D参数设置为函数参数,而不是模板参数,否则您将被迫编写Bind::Weak<decltype(f), 42>(f, obj)

(a) Bind::Weak(func, obj, args...)
(b) Bind::Weak(true, func, obj, args...)
(c) Bind::Weak(42, func, obj, args...)

[我觉得你应该可以做到这一点,而不必打两次bind,但我对你想做什么还不够了解。]