键入 trait 以获取 std::array 或 C 样式数组的元素类型

Type trait to get element type of std::array or C-style array

本文关键字:样式 数组 类型 元素 array trait 获取 std 键入      更新时间:2023-10-16

我想要一个类型特征来获取std::array或普通的旧 C 样式数组的元素类型,例如。 当提供std::array<char, 3>char[3]时,它应该返回char

这样做的机制似乎只是部分到位......我可以在std::array上使用::value_type,在普通数组上使用std::remove_all_extents,但我找不到将两者结合起来的单一类型特征,我无法自己编写一个。

我已经做到了这一点:

#include <array>
#include <type_traits>
template <class T>
using element_type =  typename std::conditional<
    std::is_array<T>::value,
    typename std::remove_all_extents<T>::type,
    typename T::value_type
>::type;

它对std::array工作得很好,当然:

int main()
{
    static_assert(
        std::is_same<char, element_type<std::array<char, 3>>>::value,
        "element_type failed");
}

但是当我传递一个普通数组时会中断,因为显然普通数组没有::value_type.

static_assert(std::is_same<char, element_type<char[3]>>::value, "element_type failed");

正如您所期望的那样,只是给出诸如"'T'::"后跟"::"之类的错误。

如果我正在编写一个函数,我会使用 std::enable_if 来隐藏有问题的模板实例化,但我看不出如何在类型特征中使用这种方法。

解决这个问题的正确方法是什么?

对于支持 std::begin 支持的任何类型的容器/数组的非常通用的解决方案:

template<typename T>
using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>;

您可能知道std::begin返回一个迭代器。取消引用它会给你它的值,你可以得到使用decltype的类型。std::remove_reference_t是必需的,因为迭代器返回对它们所指向的元素的引用。因此,这适用于std::begin具有重载的每个类型。

您可以在 std::array 和 C 样式数组上调用哪些函数/运算符? 当然operator[]

template <class Array>
using array_value_type = decay_t<decltype(std::declval<Array&>()[0])>;

这将适用于支持按整数查找的任何内容,包括std::vectorstd::map<std::size_t, T>等。

如果你想区分从 const 数组和非成本数组中获得的内容,你可能想要创建 2 个类型特征,命名如下:

template <class Array>
using array_element_t = decay_t<decltype(std::declval<Array>()[0])>;
template <class Array>
using array_value_t   = remove_reference_t<decltype(std::declval<Array>()[0])>;

这里的第二个特征保留了传入的Array类型的恒定性,而第一个特征则剥离了它。 这两者肯定都有用例。

一种方法是调度到专门的模板

template<typename>
struct arr_trait;
template<typename T, size_t N>
struct arr_trait<T[N]> {using type = T;};
template<typename T, size_t N>
struct arr_trait<std::array<T, N>> {using type = T;};
template<typename T>
struct arr_trait<T&> : arr_trait<T> {};
template<typename T>
struct arr_trait<T&&> : arr_trait<T> {};
template<typename T>
using element_type = typename arr_trait<T>::type;

std::conditional失败的原因是因为它不支持(据我所知也不能(支持短路,并且将评估这两种类型。