我有一个类似于此的类,它应该带一个函数对象并在以后使用它:
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([]{});
有关函数引用的特殊之处以及为什么重载在那里不起作用?
答案 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&
属于同一类型,因此错误。