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

use*_*436 1 c++ type-traits c++17

我需要一个 typetrait 来为以下“连续内存”类型(operator[]可以应用)返回值类型:

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

在 C++17 中是否有比下面乏味的实现更简洁的实现?就像以某种方式将所有指针案例收集在一个专业化和应用std::remove_ptr中一样,对于T[N]with 也是如此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; };
Run Code Online (Sandbox Code Playgroud)

max*_*x66 5

我需要一个 typetrait,它会返回以下“连续内存”类型的值类型(operator[] 可以应用于该类型):

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

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

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

template <typename, typename = void>
struct remove_array_like;
Run Code Online (Sandbox Code Playgroud)

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

一个用于contigous内存容器(因此用于与容器data()返回一个指针contigous含有存储器的启动方法),即对std::vectorstd::arraystd::string和其它类型的串

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

一个指针

template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
   : public contained_type<T>
 { };
Run Code Online (Sandbox Code Playgroud)

一个用于数组

template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
   : public contained_type<T>
 { };
Run Code Online (Sandbox Code Playgroud)

下面是一个完整的编译 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> );
 }
Run Code Online (Sandbox Code Playgroud)

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

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