为什么 const 数组优先绑定到 const T& 参数而不是 T&& 参数?
Why do arrays of const preferentially bind to const T& parameters instead of T&& parameters?
"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&
重载,而T&&
重载将针对所有其他参数类型调用。 但是考虑一下当我们用常量和非常量内容的数组调用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
有一个常量,但我认为这太简单了。 推导的类型 T(无论选择哪个模板(是"5 个 const int 的数组",因此const T&
重载中的 param
类型将是"引用 5 个 const int 的 const 数组"。 但是名为 constArray 的数组本身并没有声明为 const。 那么,为什么对 func(constArray)
的调用不调用T&&
重载,从而产生一个用于param
"引用 5 个 const 整数数组"的类型呢?
这个问题的动机是与 c++ 模板函数参数推导和函数解析中的问题相关的讨论,但我认为该线程在其他问题上偏离了方向,并没有澄清我现在在这里提出的问题。
在函数参数列表(以及其他任何地方(中,数组类型的 cv 限定被打乱以限定数组元素类型。 例如,对于 T = int [5]
,const T &
将转换为 int const (&) [5]
。
3.9.3 CV限定符[基本类型限定符]
2 - [...]应用于数组类型的任何 cv 限定符都会影响数组元素类型,而不是数组类型 (8.3.4(。
因此,使用类型 int const [5]
的参数对 func
的调用被推导为对以下任一参数的调用:
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 作为参数模板。 我们删除给出 A1 -> const C
和 T2 -> T
的引用 (14.8.2.4:5(,然后删除给出 A1 -> C
和 T2 -> T
的 cv 限定 (14.8.2.4:7(。 模板T
可以推导出为C
(14.8.2.4:8(,因此A1至少与P2一样专业;相反,A2 -> D
-> D
、P1 -> const T
-> T
和 T
可以推导出为D
,所以 A2 至少和 P1 一样专业。
这通常意味着两者都不比另一个更专业;但是,由于P
和A
类型是引用类型14.8.2.4:9适用,并且由于A1是左值引用而P2不是,因此T1被认为比T2更专业。 (参考类型之间的联系也可以通过同一条款下的简历限定来打破。
您混淆了右值引用(如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]
可以通过U = int[5]
实现T = U const &
。
请注意,若要将左值绑定到通用引用,必须将类型本身推导为引用类型。
(显然array
与const T &
不匹配,因为它不是常量。它只能匹配T&&
,推导出T = int (&)[5]
(。
- 将const引用参数初始化为默认参数会导致悬空引用吗
- 'HMODULE LoadLibraryA(LPCSTR)':无法将参数 1 从 'const _Elem *' 转换为 'LPCSTR'
- 将函数参数"const char*"转换为"std::string_view"是
- 将参数传递为"const"的奇怪效果
- 在函数中使用 const int size 参数创建数组会在 Visual Studio 中抛出错误 C++:表达式的计
- std::span<const T> 作为函数模板中的参数
- 视觉工作室 2017;启用 /permissive 时,类型 "const wchar_t *" 的参数与类型 "PWSTR" 的参数不兼容
- 何时应在构造函数参数中使用 const C++?
- 不允许运算符 const 参数调用 const 成员函数
- std::p ackaged_task 应该删除带有 const 参数的复制 c'tor
- 知道模板参数在编译时是否为 const char*?
- 将对象传递给函数而不将其包装到 std::ref 中,而参数被指定为 const 引用
- 具有参数 (const T *&) 或 (T * &) 或 (const T * const &) 或 (T * const &) 的方法
- 为什么在函数参数前面添加 const 会出错?
- 在函数中使用运算符重载,在 c++ 中使用 const 类型输入参数
- C++ Boost - 序列化错误 - 将"const B"作为"this"参数
- C++ 我应该如何解释函数参数long(*pPointer)(OtherClass *const, long)?
- 什么..表示函数内部参数(const char*值,..)
- 如果参数创建本地副本,则使用参数"const [variable_type] &"的目的是什么?
- 候选函数不可行:第一个参数('const Node *')将失去常量限定符