7

我想一般地“腌制”函数调用,以便以后执行它们。这些函数的返回类型将始终是void(目前)。像这样的东西:

template<typename F, typename... Args>
std::function<void()> 
pickle(F function, Args&&... args) {
    return std::bind(F, args...);
}

问题是,如果args包含 const 引用,std::bind则尝试复制构造该值,如果该类型缺少复制构造函数,这并不总是理想的,甚至是无效的。如何以std::ref用于左值引用和正常std::forward用于左值引用的方式转发参数?

例子

#include <functional>

class NonCopyable {
public:

    NonCopyable() {}

    NonCopyable(const NonCopyable&) = delete;
};

template<typename F, typename... Args>
std::function<void()>
pickle(F function, Args&&... args)
{
    return std::bind(function, std::forward<Args>(args)...);
}

int main()
{
    NonCopyable obj;
    auto f = pickle(
        [](const NonCopyable&) {},
        obj
    );
    return 0;
}

上面的代码片段不会编译,抱怨删除的复制构造函数。(我在这里使用 forward 是因为有人建议它,但似乎已经删除了他们的答案)。

4

2 回答 2

8

超载,呵呵。

// also does the correct thing for `T const`
template<class T>
std::reference_wrapper<T> maybe_ref(T& v, int){ return std::ref(v); }

// just forward rvalues along
template<class T>
T&& maybe_ref(T&& v, long){ return std::forward<T>(v); }

template<typename F, typename... Args>
std::function<void()> 
pickle(F function, Args&&... args) {
    return std::bind(function, maybe_ref(std::forward<Args>(args), 0)...);
}

int/long参数和参数消除了编译器发现重载不明确的左值大小写的0歧义,否则不会造成任何伤害。

于 2013-03-19T14:04:55.830 回答
0

这有点难看(过度使用enable_if),但它有效:

template<typename T> typename std::enable_if<
  !std::is_lvalue_reference<T>::value, T &&>::type
  forward_as_ref(typename std::remove_reference<T>::type &t) {
    return static_cast<T &&>(t);
  }
template<typename T> typename std::enable_if<
  !std::is_lvalue_reference<T>::value, T &&>::type
  forward_as_ref(typename std::remove_reference<T>::type &&t) {
    return t;
  }
template<typename T> typename std::enable_if<
  std::is_lvalue_reference<T>::value,
  std::reference_wrapper<typename std::remove_reference<T>::type>>::type
  forward_as_ref(T t) {
    return t;
  }

这是一个使用类模板专业化的版本:

template<typename T> struct forward_as_ref_type {
   typedef T &&type;
};
template<typename T> struct forward_as_ref_type<T &> {
   typedef std::reference_wrapper<T> type;
};

template<typename T> typename forward_as_ref_type<T>::type forward_as_ref(
   typename std::remove_reference<T>::type &t) {
      return static_cast<typename forward_as_ref_type<T>::type>(t);
   }
template<typename T> T &&forward_as_ref(
   typename std::remove_reference<T>::type &&t) {
      return t;
   }
于 2013-03-19T14:10:07.367 回答