c++ 常量访问引用

daolsyd0  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(145)

使用指针,我可以为任何类型T创建(const/non_const)到(T/const T)的所有组合,如this answer中详细讨论的那样。
但是使用引用时,我如何定义一个对一个可以被解引用的变量的引用(像迭代器一样),这样解引用的引用就给出了一个常量访问?
例如,考虑以下程序:

#include <array>

int main()
{
    std::array<int, 3> a{0, 0, 0};
    auto const& iter = a.begin();
    // iter++; // Error!
    (*iter)++;
}

iterconst变量,如果取消注解iter++行,代码将无法编译。但是iter解引用了一个非常数的int,实际上它可以随(*iter)++递增。在某种意义上,iter模拟int * const,即指向int的常量指针。
是否可以使用(const或non_const)引用iter,使*iter成为const int?
请记住,我知道我可以在上面的例子中使用const auto& iter = a.cbegin(),这个问题的目的是如何定义iter,这样,当解引用时,不管等号右边是什么,都可以给一个常量访问,对于迭代器,我可以想象显式绑定到const_iterator,如下所示:

const decltype(a)::const_iterator& iter = a.begin();

这个解决方案之所以有效,是因为我知道iterator的等效"常量访问"是const_iterator
但是,更一般地说:

(some type)& iter = rhs

如果我只知道*rhs是一个int,那么有没有可能找出iter的类型,使得*iter是一个const int?
值得考虑的另一点是rhs转换的可能成本。

3bygqnnd

3bygqnnd1#

是否可以使用(const或非const)引用iter,以使*iter成为const int
你调用std::array::cbegin()成员函数,它将返回一个std::array<int, 3>::const_iterator .解引用,它将给予你一个const int&

  • 然而,你不能通过非const引用来获取const_iterator,你必须通过值或const&来获取它(以延长由cbegin()函数返回的临时变量的生命期),一般的建议是通过值来获取迭代器。

有没有可能找出iter的类型,使得*iterconst int
如果你 * 知道 * iter是某种指针(const T*const T*&T*T*&等),那么你可以得到一个const T*,如下所示:

const std::remove_cvref_t<decltype(iter)> constiter = iter;

对于一般情况,从iterator获取const_iterator,而不使用容器的typedef作为const_iterator,将有点麻烦。一种方法是将迭代器 Package 在一个interator类中,该类在解引用时只返回const&。下面是一个std::list<int>::iterator的示例,它比指针稍微复杂一些。不过它对指针也有效。

#include <iostream>
#include <list>

template<class It>
struct const_iterator {
    using IterType = std::remove_reference_t<It>;
    using Traits = std::iterator_traits<IterType>;   

    using difference_type = typename Traits::difference_type;
    using value_type = const typename Traits::value_type;
    //                 ^^^^^
    using pointer = value_type*;
    using reference = value_type&;
    using iterator_category = typename Traits::iterator_category;

    const_iterator(It nc) : value(nc) {}
    
    // dereferencing gives a const&
    reference operator*() const { return *value; }

    bool operator==(const const_iterator& rhs) const { return value == rhs.value; }
    bool operator!=(const const_iterator& rhs) const { return !(*this == rhs); }
    
    const_iterator& operator++() { ++value; return *this; }
    const_iterator operator++(int) { const_iterator rv(*this); ++value; return rv; }

    // Add conditional support for proxy member functions supported
    // by the original iterator type / iterator_category

    IterType value;
};

template<class It>
const_iterator(It) -> const_iterator<It>;

int main()
{
    std::list<int> a{1, 2, 3};
    auto iter = a.begin();
    *iter = 10;                // Ok

    const_iterator cit(iter);  // make a const_iterator from the original iterator
    ++cit;                     // Ok
    std::cout << *cit << '\n'; // prints 2
    //*cit = 20;               // Error: *cit is a `const int&`
}
yvgpqqbh

yvgpqqbh2#

这个问题的目的是如何定义iter,以便在取消引用时给予常量访问
这就是const迭代器发挥作用的地方,对于迭代器来说,这实际上是一个不太全局的问题,因为const迭代器仅仅意味着一个常量容器的标准迭代器:

const std::array<int, 3> a{ 0, 0, 0 };
static_assert(
    std::is_same_v<decltype(a.begin()), decltype(a.cbegin())>,
    "The iterators are of different type"
);

在这种情况下,迭代器自身类型的一致性实际上并不取决于它们是否指向const类型,正如你已经注意到的,const迭代器意味着迭代器本身不能被改变,不能指向不同的对象。
回答你的第二个问题:
是否可以改为使用(const或non_const)引用const int?
不,引用总是const隐式的,并且在绑定到变量之后不能被重新绑定。

相关问题