首页 文章

为什么rvalue引用参数与重载决策中的const引用匹配?

提问于
浏览
3

潜在相关文章:

对于STL容器 Cstd::begin(C) 和类似的访问函数(包括 std::data(C) (因为C 17))应该具有 C::begin() 和其他对应的 C 方法的相同行为 . 但是,由于涉及lvalue / rvalue引用和constness的重载决策的细节,我观察了一些有趣的行为 .

DataType1int* ,很容易预料到 . 此外,通过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) 的行为方式相同 .


我的一些研究:为了获得 DataType3int*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 回答

  • 2

    此行为归因于重载解析规则 . 根据标准8.5.3 / p5.2引用[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*

    您不能将临时绑定到非const左值引用 .

  • 2

    唯一令人惊讶的是 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中使用右值引用改变了这种情况,但这一点仍然有效) .

    这是一个例子:

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

相关问题