为何无法从std::iterator_traits获取value_type?代码报错求助
是的,这个错误确实是const修饰foo引发的,但背后的原因有点绕——不是const修饰指针指向的内容,而是你给指针本身加了const,导致decltype(foo)的类型让std::iterator_traits无法正确识别。
为什么会报错?
我们一步步拆解你的代码:
std::cbegin(arr)针对C风格数组返回的是const int*(本质就是指向const元素的指针,也就是所谓的const迭代器)。- 当你写
const auto foo = cbegin(arr);时,auto会推导出const int*,再加上const修饰变量foo,最终foo的类型是const int* const——意思是这个指针本身是不可修改的(你没法做foo++这种操作),但这完全是画蛇添足:cbegin已经保证了你不能通过foo修改数组里的元素,给指针本身加const没有额外意义,反而给iterator_traits添了麻烦。
MSVC在这里的处理是,把const int* const当成了指针的const引用类型(从错误信息里的std::iterator_traits<_Ty *const &>就能看出来),而标准库的iterator_traits只针对普通指针类型(比如T*或const T*)做了特化,所以找不到value_type成员。
解决办法
有几种方式可以解决这个问题,按推荐程度排序:
最直接:去掉
foo的const修饰
既然cbegin已经返回了const迭代器,完全没必要给指针本身加const:const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; auto foo = std::cbegin(arr); // 去掉前面的const const typename std::iterator_traits<decltype(foo)>::value_type bar = 1;此时
foo的类型是const int*,iterator_traits<const int*>::value_type就是你预期的int,编译完全正常。如果坚持保留
foo的const:剥离指针的const限定
用std::remove_const_t去掉指针本身的const属性,让iterator_traits能识别:#include <type_traits> // 需要包含这个头文件 const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; const auto foo = std::cbegin(arr); const typename std::iterator_traits<std::remove_const_t<decltype(foo)>>::value_type bar = 1;std::remove_const_t<const int* const>会转换成const int*,这样iterator_traits就能正确找到value_type。更通用的方案:用
std::decay_t处理类型std::decay_t会自动去掉类型的引用、const/volatile限定,直接得到原始的指针类型,适用性更广:#include <type_traits> const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; const auto foo = std::cbegin(arr); const typename std::iterator_traits<std::decay_t<decltype(foo)>>::value_type bar = 1;
额外小技巧
其实你完全可以不用iterator_traits,直接通过decltype(*foo)来推导元素类型,再去掉const和引用:
#include <type_traits> const int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; const auto foo = std::cbegin(arr); const std::remove_cv_t<std::remove_reference_t<decltype(*foo)>> bar = 1;
在C++14及以后,用std::decay_t<decltype(*foo)>也能直接得到int,写法更简洁。
内容的提问来源于stack exchange,提问作者Jonathan Mee

