为什么const数组优先与const T&参数代替T&&参数?

时间:2012-09-11 17:17:10

标签: c++ c++11

使用“T&&”重载功能模板通常是一个坏主意。参数,因为它可以绑定到任何东西,但我们假设我们仍然这样做:

template<typename T>
void func(const T& param)
{
  std::cout << "const T&\n";
}

template<typename T>
void func(T&& param)
{
  std::cout << "T&&\n";
}

我的理解是,const T&重载将被调用为const lvalues的参数,并且将为所有其他参数类型调用T&&重载。但是考虑当我们使用const和非const内容数组调用func时会发生什么:

int main()
{
  int array[5] = {};
  const int constArray[5] = {};

  func(array);             // calls T&& overload
  func(constArray);        // calls const T& overload
}

VC10,VC11和gcc 4.7同意显示的结果。我的问题是为什么第二个调用会调用const T&重载。简单的答案是constArray中有一个const,但我认为这太简单了。推导出的类型T(无论选择何种模板)都是“5个const整数的数组”,因此param重载中const T&的类型将是“对5个const整数的const数组的引用” 。但是名为constArray的数组本身并不是const。那么为什么调用func(constArray)不会调用T&&重载,从而产生param“引用5个const整数数组”的类型?

这个问题是由与c++ template function argument deduce and function resolution问题相关的讨论推动的,但我认为这个问题在其他问题上存在偏见,并没有澄清我现在在这里提出的问题。

2 个答案:

答案 0 :(得分:7)

在函数参数列表(以及其他任何地方)中,对数组类型的cv限定进行了右移以限定数组元素类型。例如,对于T = int [5]const T &会转换为int const (&) [5]

  

3.9.3 CV-quali firs [basic.type.quali fi er]

     

2 - [...]应用于数组类型的任何cv限定符都会影响数组元素类型,而不是数组类型(8.3.4)。

因此,funcint const [5]类型参数的调用被推断为对以下任何一个的调用:

void func<int [5]>(int const (&) [5])
void func<int const (&) [5]>(int const (& &&) [5])
// where the above collapses to
// 'void func<int const (&) [5]>(int const (&) [5])'

两种重载都是可行的,但前者是首选:

设T1为const T &模板,T2为T &&模板;也就是说,它们的参数类型是T1:= const T &和T2:= T &&。然后,对于合成类型const C &D &&,转换后的参数类型(14.5.6.2:3)可以写成A1:= C,A2:= D

现在,我们尝试针对T2(14.8.2.4:2)对T1进行排序,首先使用A1作为参数模板,使用P2作为参数模板。我们删除了引用A1 - &gt;的引用(14.8.2.4:5)。 const C和T2 - &gt; T,然后删除cv-qualification(14.8.2.4:7),给出A1 - &gt; C和T2 - &gt; T。模板T可以推导为C(14.8.2.4:8),因此 A1至少与P2 一样专业;相反,A2 - > D - &gt; D,P1 - &gt; const T - &gt; TT可以推断为D,因此 A2至少与P1 一样专业。

这通常意味着两者都不比另一个更专业;但是,因为PA类型是引用类型14.8.2.4:9适用,并且因为A1是左值引用而P2不是,所以T1被认为比T2更专业。 (引用类型之间的联系也可以通过同一条款下的cv-qualification来打破。)

答案 1 :(得分:3)

您混淆右值引用(如int&&)和通用引用(由模板参数构成,如template <typename T> ... T&&中)。

Rvalue引用确实不会绑定到左值。但是通用引用绑定到任何东西。问题是谁更合适。

您拥有的类型是int const [5]。现在让我们看看:

  • 针对T const &:与T = int[5]匹配。

  • 针对T &&:与T = int const (&)[5]匹配。

前者是更好的匹配,在以下意义上:两个模板都产生相同的重载。但T = int[5] T = int const (&)[5] 更专业。您可以看到这一点,因为T = int const (&)[5]可以通过T = U const &实现为U = int[5]

请注意,要将左值绑定到通用引用,必须将类型本身推断为引用类型。

(显然arrayconst T &不匹配,因为它不是常量。它只能匹配T&&,推断T = int (&)[5])。