问题描述
如果我想获取数组的大小(元素数),我可以使用 sizeof(a) / sizeof(a[0])
但新标准可以使用类型特征来做到这一点:
int main(){
int a[]{5,7,2,3,6,7};
std::cout << std::extent<decltype(a)>::value << '\n'; // 6
auto& refA = a;
std::cout << std::extent<decltype(refA)>::value << '\n'; // 0
std::cout << sizeof(refA) / sizeof(refA[0]) << '\n'; // 6
std::cout << std::extent<std::remove_reference<decltype(refA)>::type>::value << '\n'; // 6
std::cout << "\ndone!\n";
}
一切正常,但为什么 std::extent
不能像 sizeof()
运算符那样处理对数组的引用?我必须删除上一个示例中的引用才能获得实际的数组类型。
解决方法
这并没有直接回答问题,而是一个潜在的xy problem。你不应该也不需要使用 std::extent 来做到这一点。在这种情况下它没有用。使用 std::size 代替:
std::size(a)
现在来回答:
为什么 std::extent 对数组的引用不起作用
它有效;只是不是您可能一直期望的方式。特别是,任何引用的范围都是 0,而不是被引用类型的范围。
,老实说,原因在于执行和标准; 看看这个的 GCC 实现:
/// extent
template<typename,unsigned _Uint>
struct extent
: public integral_constant<std::size_t,0> { };
template<typename _Tp,unsigned _Uint,std::size_t _Size>
struct extent<_Tp[_Size],_Uint>
: public integral_constant<std::size_t,_Uint == 0 ? _Size : extent<_Tp,_Uint - 1>::value>
{ };
template<typename _Tp,unsigned _Uint>
struct extent<_Tp[],_Uint == 0 ? 0 : extent<_Tp,_Uint - 1>::value>
{ };
甚至 cppreference 中指定的可能实现:
template<class T,unsigned N = 0>
struct extent : std::integral_constant<std::size_t,0> {};
template<class T>
struct extent<T[],0> : std::integral_constant<std::size_t,0> {};
template<class T,unsigned N>
struct extent<T[],N> : std::extent<T,N-1> {};
template<class T,std::size_t I>
struct extent<T[I],I> {};
template<class T,std::size_t I,unsigned N>
struct extent<T[I],N-1> {};
T[]
不同于对数组的引用;所以它退回到默认的,就是这个:
template<class T,unsigned N = 0>
struct extent : std::integral_constant<std::size_t,0> {};
始终为 0。
也可以自定义它以支持引用,但是您可能希望它支持 const
和 volatile
和 const volatile
以及实现者(如果它是我)是这样的(from my web++ project):
WEBPP_REMOVE_CVREF()
WEBPP_REMOVE_CVREF(const)
WEBPP_REMOVE_CVREF(volatile)
WEBPP_REMOVE_CVREF(const volatile)
WEBPP_REMOVE_CVREF(&)
WEBPP_REMOVE_CVREF(&&)
WEBPP_REMOVE_CVREF(const&)
WEBPP_REMOVE_CVREF(const&&)
WEBPP_REMOVE_CVREF(volatile&)
WEBPP_REMOVE_CVREF(volatile&&)
WEBPP_REMOVE_CVREF(const volatile&)
WEBPP_REMOVE_CVREF(const volatile&&)
是的,这样做不是很好吗?因此,实际上将这一点放在用户而不是库实现者或标准上会更方便,而且通常不会造成混淆。