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

Rvalue reference overloading when base type is function reference

本文关键字:引用 重载 类型 函数 基类      更新时间:2023-10-16

我有一个类似的类,应该接受一个函数对象并稍后使用它:

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

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

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

函数引用有什么特别之处,为什么重载不起作用?

clang的错误消息更有启发性:

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

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

中的引用崩溃规则

6 -如果[…]类型TR[是]对类型T的引用,尝试创建类型"对cv TR的左值引用"会创建类型"对T的左值引用",而尝试创建类型"对cv TR的右值引用"会创建类型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));
}

F = void(&)(), F&&const F&是相同的类型,因此错误。