潜在相关文章:
对于STL容器 C
, std::begin(C)
和类似的访问函数(包括 std::data(C)
(因为C 17))应该具有 C::begin()
和其他对应的 C
方法的相同行为 . 但是,由于涉及lvalue / rvalue引用和constness的重载决策的细节,我观察了一些有趣的行为 .
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
返回 const 迭代器,即使 T
不是const .
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*
Question :这种差异的原因是什么?我期望 C.data()
和 std::data(C)
的行为方式相同 .
我的一些研究:为了获得 DataType3
的 int*
, T
必须明确地转换为非const左值引用类型 . 我试过 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 rvalue引用)与 const T&
版本匹配,而不是 T&
版本 .
在标准(13.3,over.match)中找到这个特定的重载决策规则并不容易 . 如果有人能指出这个问题的确切规则,那就更清楚了 .
2 回答
此行为归因于重载解析规则 . 根据标准8.5.3 / p5.2引用[dcl.init.ref],rvalue引用绑定到const lvalue引用 . 在这个例子中:
您向
std::data
提供了一个右值 . 因此,由于过载解决规则过载:是一个更好的匹配 . 因此你得到
const int*
您不能将临时绑定到非const左值引用 .
唯一令人惊讶的是
using DataType1 = decltype(T().data()); // int*
....但它仍然正常,可以在临时对象上调用成员函数而不将其视为
const
. This is another non trivial difference between member functions and free functions.例如,在C 98(pre-rvalue refs)中,无法执行
std::ofstream("file.txt") << std::string("text")
因为operator<<
不是成员而临时被视为const
. 如果operator<<
是std::ofstream
的成员,那将是可能的(甚至可能有意义) . (后来在C 11中使用右值引用改变了这种情况,但这一点仍然有效) .这是一个例子:
临时构造和使用对象时会暴露该行为 . 在我可以想象的所有其他情况下,成员
f
相当于f
自由函数 . (r-value
参考资格&&
- 对于成员和函数 - 可以给你一个更细粒度的控制,但它不是问题的一部分 . )