让我们考虑一下普通的完美转发:
class Test
{
public:
Test() = default;
Test(Test const&) { std::cout << "copy\n"; }
Test(Test&&) { std::cout << "move\n"; }
};
void test(Test)
{ }
template <typename T>
void f(T&& t)
{
test(std::forward<T>(t));
}
int main()
{
std::cout << "expect: copy\n";
Test t;
f(t);
std::cout << "expect: move\n";
f(Test());
return 0;
}
到目前为止一切都很好。但是如果我们现在引入一个函数指针,我就会遇到一个问题,我似乎无法声明普遍的(转发)参考文献 https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers:
decltype(&f<Test>) ptr = &f<Test>;
// above produces ordinary r-value references
// making below fail already on compilation:
ptr(t);
模板函数指针也会出现问题:
template <typename T>
void(*ptr)(T&& t) = &f<T>; // again resolved to r-value reference
首先,他们也决定纯右值引用,此外他们还定义了一个bunch指针而不是单个指针,使得该方法在类范围内不可用:
class C
{
template <typename T>
void(*ptr)(T&& t); // fails (of course...)
};
所以现在的问题是:是否有可能通过函数指针进行间接完美转发?
承认,已经担心答案是'no'(目前又回到左值引用),但仍然希望忽略某个地方的某些东西......
A 转发参考不仅仅是一个假设的概念。它是给特定类型的右值引用的名称,该类型在模板参数推导中具有特殊的推导规则。
具体根据[临时扣除呼叫]/3 https://timsong-cpp.github.io/cppwp/n4861/temp#deduct.call-3.sentence-3:
A 转发参考是对 cv 无限定模板参数的右值引用,该参数不表示类模板的模板参数(在类模板参数推导 ([over.match.class.deduct]) 期间)。如果 P 是转发引用并且参数是左值,则使用类型“对 A 的左值引用”代替 A 进行类型推导。
除了这一特殊规则(以及另一项[温度扣除类型] https://timsong-cpp.github.io/cppwp/n4861/temp#deduct.type-10.sentence-2),转发引用的行为就像任何其他右值引用一样。
特别是在提供模板参数时T
,没有发生模板参数推导,直接执行替换。作为替代,应用通常的参考折叠规则。
So f<Test>
将产生函数参数Test&&
这是一个右值引用并且f<Test&>
将产生函数参数Test&
(崩溃自&&
被应用到Test&
).
这些也是在没有显式模板参数的函数调用中为右值参数和左值参数推导的模板参数。如果引用不是转发引用,则T
永远不能推导为引用,并且函数参数在替换后始终是右值引用。此次专项调整A
引用中提到的允许T
被推导为左值引用类型,以便折叠规则也将产生左值引用函数参数。
转发引用只能存在于模板中。模板的函数或特化不能具有转发引用。它们在某种程度上并不是与右值/左值引用不同的引用类别。它们仅通过为参数的不同值类别推导出不同类型并为每个值类别生成不同的专业化来在模板中工作。
因此,由于函数指针必须指向function,而不是函数模板,在获取函数指针时必须决定选择参数值类别的两个特化中的哪一个。
如果需要任何类型的推导,那么函数指针就无法提供该推导。相反,应该使用 lambda 或函子类型,它可以执行推导,并可以根据值类别选择要调用的函数或函数模板特化。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)