基类型为函数引用时的右值引用重载

时间:2014-07-03 08:44:59

标签: c++ c++11

我有一个类似于此的类,它应该带一个函数对象并在以后使用它:

template<typename F>
class A {
public:
  A(const F& f) : _f(f) {}
  A(F&& f) : _f(std::move(f)) {}

private:
  F _f;
};

我还定义了一个便利初始化函数和一个虚拟测试函数:

template<typename F>
A<F> MakeA(F&& f) {
  return A<F>(std::forward<F>(f));
}

void foo() {}

我收到一条错误消息,当我打电话时,A<F>::A(F&&) A<F>::A(const F&)无法重载F = void(&)()

auto a = MakeA(foo);

我明白我可以通过使用函数指针而不是函数引用来修复它,并且lambdas也可以很好地工作:

auto a1 = MakeA(&foo);
auto a2 = MakeA([]{});

有关函数引用的特殊之处以及为什么重载在那里不起作用?

Test code here.

2 个答案:

答案 0 :(得分:4)

来自clang的错误消息更具启发性:

  

6:错误:'A'的多次重载实例化到同一个签名'void(void(&amp;&amp;)())'

这是因为对于任何左值引用类型T = U&T&&T const&是相同的类型,根据中的引用折叠规则[dcl.ref]

  

6 - 如果类型TR [是]对类型T的引用,则尝试创建类型“对cv TR的左值引用”会创建类型“对T的左值引用”,而尝试创建类型“对cv TR的rvalue引用”创建类型TR [...]

using T = int&;
static_assert(std::is_same<T&&, T const&>::value, "!!");

真正的问题是你在其适用范围之外应用“通用参考/ std::forward”模式;它仅适用于完美转发参数。如果你传递一个lambda左值,你的代码也会中断:

auto l = []{};
auto d = MakeA(l);    // breaks

编写类型推导构造函数的正确方法是将decay应用于参数的类型(参见make_optional):

template<typename F>
A<typename std::decay<F>::type> MakeA(F&& f) {
  return A<typename std::decay<F>::type>(std::forward<F>(f));
}

答案 1 :(得分:1)

F = void(&)()F&&const F&属于同一类型,因此错误。