如何使用引用使模板类型推导工作?

shr*_*ike 7 c++ templates c++11 c++14

我有一个模板功能,func:

template<typename T>
void func(T p)  { f(p); }
Run Code Online (Sandbox Code Playgroud)

还有一套功能f:

f(SomeType&);
f(int);
...
Run Code Online (Sandbox Code Playgroud)

如果我实例化模板函数,func使用引用作为函数参数p,而不显式指定模板参数T,那么推导出的类型将不是引用类型p,而是类型p是对它的引用,例如:

SomeType s;
SomeType& ref_to_s = s;
func(ref_to_s);             // Type deduction results in: func<SomeType>(ref_to_s)
func<SomeType&>(ref_to_s);  // Need to explicitly specify type to work with reference
Run Code Online (Sandbox Code Playgroud)

所以,我的问题是:

  • 为什么编译器SomeType&在上述情况下无法推断出引用类型?
  • 有没有办法定义模板函数func,因此类型推导与引用类型一起使用,而没有明确指定模板参数T

要清楚,我想要两者兼顾(参见f上面的函数):

func(ref_to_s);    // Calls func<SomeType&>(ref_to_s)
func(1);           // Calls func<int>(1)
Run Code Online (Sandbox Code Playgroud)

Ker*_* SB 9

第一部分的答案是从值初始化对象并将引用绑定到值被认为是同样好的(两者都是重载决策中的"完全匹配"),因此不推导出参数的引用.相反,您必须准确指定它.对于相关示例,请考虑假设的构造

T x = f();
Run Code Online (Sandbox Code Playgroud)

T这个思想实验的持续时间里,你应该推断出什么.应该x是什么- 一个对象或一个引用?它取决于什么?当然,右边的表达式有一个值,该值的类型始终是对象类型.你应该如何决定?您可以查询表达式的值类别,但这太微妙了.所以相反,必须说出你想要的东西:

T x = f();    // x is an object
T & r = f();  // r is a reference
Run Code Online (Sandbox Code Playgroud)

在C++中,如果替换为T,则实际上可以使用此机制auto.相同的规则适用于函数模板的模板参数推导.

现在就如何解决你的问题.通常的习惯用法是让函数模板始终采用引用,然后将参数转发给内部调用:

template <typename T>
void func(T && t)
{
    f(std::forward<T>(t));
};
Run Code Online (Sandbox Code Playgroud)

现在func的参数总是在任何专门的引用(但由于一个奇怪的规则称为'参考塌陷’,它可以是一个左值或右值参考),并且该值被转发具有相同值的类别组f,并且这过载分辨率f允许当原始参数来选择左值引用过载func是一个左值.

  • @bipll:没有这样的事情.函数调用表达式*是表达式*,表达式始终具有对象类型.函数返回类型的引用限定条件确定表达式的值类别.一个函数不能"返回引用",这只是一个草率的口语. (6认同)
  • @ JohannesSchaub-litb:对不起,我很滑稽.它的意思是"我知道,但不想详细说明".我不想让我的观点与其他类型的表达式复杂化,我希望教训是表达式永远不会引用.你当然是正确的,表达式可以有多种类型. (3认同)

Naw*_*waz 7

使用转发参考:

template<typename T>
void func(T && p)    //notice &&
{ 
    f(std::forward<T>(p));  //notice std::forward
}
Run Code Online (Sandbox Code Playgroud)

现在请搜索转发引用(或之前提到的通用引用)以及std::forward此站点以了解有关它们的更多信息.