Why rvalue reference argument matches to const reference in overload resolution?

时间:2015-12-09 10:17:48

标签: c++11 const c++14 rvalue-reference c++17

Potentially related articles:

对于STL容器Cstd::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

我尝试了int const*,它也可以处理数组和非STL容器。但它会产生 std::begin 。同样,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* 不是const。

C.data()

问题:这种差异的原因是什么?我希望std::data(C)int*的行为方式相同。

我的一些研究:为了获得DataType3的{​​{1}},必须明确地将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 个答案:

答案 0 :(得分:2)

此行为归因于重载解析规则。根据 standard 8.5.3 / p5.2 References [dcl.init.ref] ,rvalue引用绑定到const lvalue引用。在这个例子中:

std::data(T())

您向std::data提供左值。因此,由于过载解决规则导致过载:

template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }

是一个更好的匹配。因此,您获得了const int*

您不能将临时绑定到非常量左值引用。

答案 1 :(得分:2)

唯一令人惊讶的是using DataType1 = decltype(T().data()); // int*

...但它仍然正常,成员函数可以在临时对象上调用,而不会被视为const这是成员函数和自由函数之间的另一个非常重要的区别。

例如,在C ++ 98(pre-rvalue refs)中,无法执行std::ofstream("file.txt") << std::string("text")因为operator<<不是成员而临时被视为const。如果operator<<std::ofstream的成员,则可能(甚至可能有意义)。 (后来在C ++ 11中使用rvalue引用改变了这种情况,但这一点仍然有效。)

这是一个例子:

#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参考资格&& - 对于成员和函数 - 可以为您提供更细粒度的控制,但它不是问题的一部分。)