如何定义引用,使其解引用后获得const访问权限?
使用指针时,可以针对任意类型T组合出const/非const与T/const T的所有搭配。但使用引用时,如何定义可解引用类型(比如迭代器)的引用,使得解引用后的结果具备const访问权限?
举个例子:
#include <array> int main() { std::array<int, 3> a{0, 0, 0}; auto const& iter = a.begin(); // iter++; // 错误!iter是const引用,无法修改自身 (*iter)++; // 可以执行,解引用得到非const int }
这段代码里,iter是绑定到普通迭代器的const引用,自身无法修改(比如iter++会报错),但解引用后得到的是非constint,类似int * const(指向int的const指针)的行为。
需求是:无论右侧表达式是什么,定义一个引用iter,使得*iter始终是const类型(比如const int)。已知用const auto& iter = a.cbegin()可以实现,但需要更通用的方案——即使右侧是a.begin()这类返回非const迭代器的表达式,也能让解引用结果变成const。
对于迭代器,显式绑定到const_iterator是可行的:
const decltype(a)::const_iterator& iter = a.begin();
但这个方案依赖于已知迭代器对应的const版本是const_iterator。现在需要更通用的场景:
(some type)& iter = rhs
如果仅知道*rhs是int,如何确定some type,使得*iter为const int,同时尽量避免转换开销?
1. 针对标准库迭代器的直接方案
利用标准库迭代器的隐式转换特性,将普通迭代器转换为const_iterator后绑定到引用:
#include <array> int main() { std::array<int, 3> a{0, 0, 0}; // 将a.begin()返回的iterator隐式转换为const_iterator,绑定到const引用 const typename decltype(a)::const_iterator& iter = a.begin(); // (*iter)++; // 编译错误:解引用得到const int&,无法修改 iter++; // 可以执行:const引用仅限制修改iter本身,引用的const_iterator对象仍可调用非const成员函数 }
如果需要连iter++也禁止(即迭代器自身也不能修改),可以把类型改为const typename decltype(a)::const_iterator const&,此时iter++会因对象是const而报错。
2. 通用类型推导方案
如果不想手动指定容器类型,可以用auto结合static_cast实现:
#include <array> int main() { std::array<int, 3> a{0, 0, 0}; // 显式转换为const_iterator后绑定到const引用 const auto& iter = static_cast<decltype(a)::const_iterator>(a.begin()); // (*iter)++; // 错误:解引用结果为const int }
3. 任意可解引用类型的通用模板方案
对于自定义可解引用类型(比如自定义指针类),可以通过类型萃取模板统一处理:
#include <type_traits> #include <iterator> // 类型萃取模板:获取可解引用类型的const版本 template<typename T> struct const_deref_type { // 默认适配标准库迭代器 using type = typename std::iterator_traits<T>::const_iterator; }; // 特化指针类型 template<typename T> struct const_deref_type<T*> { using type = const T*; }; // 定义别名简化使用 template<typename T> using const_deref_t = typename const_deref_type<T>::type; // 使用示例 int main() { int arr[3] = {0,0,0}; int* rhs = arr; // 绑定到const版本的可解引用类型引用 const const_deref_t<decltype(rhs)>& iter = rhs; // (*iter)++; // 错误:解引用得到const int& iter++; // 可以执行:const int*指针本身可移动 }
关于转换开销
标准库中iterator到const_iterator的转换是无开销的隐式转换,仅做类型匹配,不会产生内存拷贝。自定义可解引用类型只要转换是浅拷贝(比如指针类),开销也可忽略。绑定到const引用还能延长临时转换对象的生命周期,避免不必要的拷贝。
内容的提问来源于stack exchange,提问作者francesco

