为什么在重载解析中右值引用参数与常量引用匹配
Why rvalue reference argument matches to const reference in overload resolution?
潜在相关文章:
- 对象、右值引用、常量引用之间的重载解析
- std::begin和R值
对于STL容器C
,std::begin(C)
和包括std::data(C)
在内的类似访问函数(由于C++17)应该具有与C::begin()
和其他相应的C
的方法相同的行为。然而,由于涉及左值/右值引用和常量的重载解析的细节,我观察到了一些有趣的行为。
DataType1
是int*
,这是很容易预料到的。此外,通过Boost的type_id_with_cvr
确认了。const vector<int>
给了int const*
一点也不奇怪。
using T = vector<int>;
using DataType1 = decltype(T().data()); // int*
using CT = const T;
using DataType2 = decltype(CT().data()); // int const*
using boost::typeindex::type_id_with_cvr;
cout << type_id_with_cvr<DataType1>() << endl; // prints int*
...
我尝试了std::data
,它也可以处理数组和非STL容器。但它产生int const*
。类似地,std::begin
返回一个常量迭代器,即使T
不是常量。
using T = vector<int>;
using DataType3 = decltype(std::data(T())); // int const* Why ???
using CT = const T;
using DataType4 = decltype(std::data(CT())); // int const*
问题:这种差异的原因是什么?我期望CCD_ 15和CCD_。
我的一些研究:为了得到DataType3
的int*
,必须将T
显式转换为非常值引用类型。我试过declval
,它很管用。
using DataType3 = decltype(std::data(std::declval<T&>())); // int*
std::data
提供两种过载:
template <class _Cont> constexpr
auto data(_Cont& __c) -> decltype(__c.data()) { return __c.data(); }
// non-const rvalue reference argument matches to this version.
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }
在解析std::data
的重载函数时,T()
(非常值引用)与const T&
版本而不是T&
版本匹配。
在标准中找到这个特定的过载解决规则并不容易(13.3,over.match)。如果有人能指出这个问题的确切规则,就会清楚得多。
这种行为归因于过载解决规则。根据标准8.5.3/p5.2引用[dcl.init.ref],右值引用绑定到常量左值引用。在本例中:
std::data(T())
您向std::data
提供了一个右值。因此,由于过载解析规则,过载:
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }
是更好的匹配。因此您得到const int*
不能将临时值绑定到非常量左值引用。
唯一稍微令人惊讶的一行是using DataType1 = decltype(T().data()); // int*
。
但这仍然是正常的,成员函数可以在临时对象上调用,而不被视为const
这是成员函数和自由函数之间的另一个重要区别
例如,在C++98(预右值参考)中,由于operator<<
不是成员,临时被视为const
,因此不可能执行std::ofstream("file.txt") << std::string("text")
。如果operator<<
是std::ofstream
的成员,这是可能的(甚至可能是有意义的)。(后来在C++11中,情况发生了变化,使用了右值引用,但该点仍然有效)。
这里是一个例子:
#include<iostream>
struct A{
void f() const{std::cout << "const member" << std::endl;}
void f(){std::cout << "non const member" << std::endl;}
};
void f(A const&){std::cout << "const function" << std::endl;}
void f(A&){std::cout << "non const function" << std::endl;}
int main(){
A().f(); // prints "non const member"
f(A()); // prints "const function"
}
当临时构造和使用对象时,行为将被暴露。在我可以想象的所有其他情况下,成员f
等价于f
的自由函数。(r-value
引用资格&&
——用于成员和函数——可以为您提供更细粒度的控制,但这不是问题的一部分。)
- 将const引用参数初始化为默认参数会导致悬空引用吗
- 具有常量引用参数的函数模板专用化
- C++:常量引用参数
- 字符串引用参数的效率C++
- 通过非常量引用参数修改常量引用参数
- 如何将指针变量作为引用参数传递?
- C++初始化 std::function 时如何将占位符绑定到引用/引用参数?
- 移动类的成员作为常量引用参数传递
- C++带有适用于左值和右值的引用参数的函数
- constexpr 函数的常量引用参数:gcc/msvc vs clang/icc
- 如何使用类型特征将函数的通用引用参数限制为 r 值引用?
- 委托构造函数和引用参数
- 对 const 引用参数使用默认值会导致崩溃
- 为什么我们不允许将纯引用参数传递给 std::thread,但允许传递原始指针?
- 为什么我需要将默认引用参数定义为 const 以便为其分配一个左值?
- 将非左值作为常量引用参数传递.临时是在本地作用域还是在调用方作用域中创建的?
- 如何强制函数仅接受左值引用参数
- 模板引用参数推断失败C++
- 非类型引用参数可以在运行时修改,这是否意味着模板可以在运行时实例化?
- 将unique_ptr作为引用参数或常量传递unique_ptr引用