Typetrait 获取连续内存容器的值类型

Typetrait to get value type of contiguous-memory containers

我需要一个类型特征,它将 return 值类型用于以下“连续内存”类型(可以应用 operator[]):

std::vector<T, Args...>std::array<T, N>T[]T[N]T*(可能带有 CV 限定符,如 T* const)。对于上述所有类型,特征应该 return T

在C++17中有没有比下面繁琐的实现更简洁的实现?就像以某种方式在一个专业化中收集所有指针案例并应用 std::remove_ptr,与 T[N]std::remove_extent 相同。

template<class T> struct remove_array_like;
template<class T> struct remove_array_like<T*> { using type = T; };
template<class T> struct remove_array_like<T* const> { using type = T; };
template<class T> struct remove_array_like<T* volatile> { using type = T; };
template<class T> struct remove_array_like<T* const volatile> { using type = T; };

template<class T> struct remove_array_like<T[]> { using type = T; };
template<class T, std::size_t N> 
struct remove_array_like<T[N]> { using type = T; };

template<class T, std::size_t N>
struct remove_array_like<std::array<T, N>> { using type = T; };

template<class T, class... Args>
struct remove_array_like<std::vector<T, Args...>> { using type = T; };

I need a typetrait that would return value type for the following "contiguous-memory" types (to which operator[] can be applied):

要“提取”所需的类型,在我看来您可以简单地使用 operator[] 本身,删除引用、volatiles 和 const。请参阅以下帮助程序结构

template <typename T>
struct contained_type
 { using type = std::remove_cv_t<
                   std::remove_reference_t<
                      decltype(std::declval<T>()[0])>>; };

您可以使用 SFINAE 编写 contained_type 结构,因此需要额外的默认模板参数

template <typename, typename = void>
struct remove_array_like;

在我看来,您只需要三个专业。

一个用于连续内存容器(因此对于具有 data() 方法的容器,return 一个指向连续内存开始的指针),即用于 std::vectorstd::arraystd::string等字符串类型

template <typename T>
struct remove_array_like<T, std::void_t<decltype(std::declval<T>().data())>>
   : public contained_type<T>
 { };

一个用于指点

template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
   : public contained_type<T>
 { };

还有一个用于数组

template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
   : public contained_type<T>
 { };

下面是一个完整的编译C++17的例子

#include <set>
#include <array>
#include <deque>
#include <vector>
#include <string>
#include <type_traits>

template <typename T>
struct contained_type
 { using type = std::remove_cv_t<
                   std::remove_reference_t<
                      decltype(std::declval<T>()[0])>>; };

template <typename, typename = void>
struct remove_array_like;

template <typename T>
struct remove_array_like<T, std::void_t<decltype(std::declval<T>().data())>>
   : public contained_type<T>
 { };

template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
   : public contained_type<T>
 { };

template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
   : public contained_type<T>
 { };

int main ()
 {
   using T1 = remove_array_like<std::vector<int>>::type;
   //using T2 = remove_array_like<std::deque<int>>::type; // error! no data(), no contigous
   using T3 = remove_array_like<std::array<int, 1u>>::type;
   using T4 = remove_array_like<std::string>::type;
   using T5 = remove_array_like<int * volatile>::type;
   using T6 = remove_array_like<int const [1u]>::type;
   using T7 = remove_array_like<int volatile []>::type;
   // using T8 = remove_array_like<std::set<int>>::type; // error!
   // using T9 = remove_array_like<int>::type; // error!

   static_assert( std::is_same_v<T1, int> );
   static_assert( std::is_same_v<T3, int> );
   static_assert( std::is_same_v<T4, char> );
   static_assert( std::is_same_v<T5, int> );
   static_assert( std::is_same_v<T6, int> );
   static_assert( std::is_same_v<T7, int> );
 }

从C++20开始可以使用std::remove_cvref,所以contained_type可以简化如下

template <typename T>
struct contained_type
 { using type = std::remove_cvref_t<
                   decltype(std::declval<T>()[0])>; };