具有默认返回值的C ++弱仿函数

时间:2014-05-01 15:30:01

标签: c++ templates functor

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

// 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...)

3 个答案:

答案 0 :(得分:0)

我能想到的唯一可以帮助您解决此问题的方法是为您可能支持的每种非类型模板参数重载非虚拟版本的Weak。在你的例子中,这是一个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

基本上问题是像&#39; 42&#39;并且&#39; false&#39;不会绑定到&#39; typename&#39;。因此,您需要该函数的专用模板来接受这些值,然后您可以在需要使用decltype()时获取该值的类型。这将消除有时需要一个参数(即:Get&lt; int&gt;)和其他两个时间的需要(即:Get&lt; int,42&gt;以指定默认值)。

另一个限制似乎可以为你的例子所接受,但在现实世界中可能是不可接受的。是模板参数本身的限制。你不能做弱&lt; foo&gt;其中foo是一个类的实例。

答案 1 :(得分:0)

我花了一些时间来解决同样的问题并提出了这个问题:

#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);
}

默认结果值有明显的限制,但它似乎在大多数时间都有效:)

答案 2 :(得分:-2)

是的,您可以使用function traits推断出可调用类型F的返回类型。

从模板参数列表中删除typename R,将typename F移至模板参数列表的开头,并将所有R替换为typename function_traits<F>::return_type。我们可以使用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,但我不太了解你想要做什么。]