5

重载带有“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 左值的参数调用T&&重载,而为所有其他参数类型调用重载。但是考虑一下当我们func使用 const 和非常量内容的数组调用时会发生什么:

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 int 的数组”,因此重载param中的类型将是“对 5 个 const int 的 const 数组的引用”。const T&但是名为 constArray 的数组本身并未声明为 const。那么为什么不func(constArray)调用T&&重载,从而产生param“对 5 个 const int 数组的引用”的类型呢?

这个问题的动机是与c++ template function argument deuce and function resolution中的问题相关的讨论,但我认为该线程在其他问题上偏离了方向,并没有澄清我现在在这里提出的问题。

4

2 回答 2

7

在函数参数列表(以及其他任何地方)中,数组类型上的 cv 限定符被改组以限定数组元素类型。例如,用T = int [5],const T &转换为int const (&) [5]

3.9.3 CV-qualiifiers [basic.type.qualifier]

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

因此,func带有类型参数的int 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 &&。然后转换后的参数类型 (14.5.6.2:3) 可以写成 A1 := const C &, A2 :=D &&合成类型C, D.

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

这通常意味着没有一个比另一个更专业;但是,因为PandA类型是引用类型 14.8.2.4:9 适用,并且由于 A1 是左值引用而 P2 不是,所以 T1 被认为比 T2 更专业。(引用类型之间的联系也可以通过同一子句下的 cv 限定来打破。)

于 2012-09-11T17:35:37.870 回答
3

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

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

您拥有的类型是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]

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

(显然array不匹配const T &,因为它不是const。它只能匹配T&&,推导T = int (&)[5])。

于 2012-09-11T17:21:40.767 回答