Как работает рекурсия при определении многомерных массивов шаблонов?

#c #arrays

#c #массивы

Вопрос:

Это код, который я нашел в Интернете.

 template<class T, unsigned ... RestD> struct array;

template<class T, unsigned PrimaryD >
struct array<T, PrimaryD>
{
typedef T type[PrimaryD];
type data;
Tamp; operator[](unsigned i) { return data[i]; }

};

template<class T, unsigned PrimaryD, unsigned ... RestD >
struct array<T, PrimaryD, RestD...>
{
typedef typename array<T, RestD...>::type OneDimensionDownArrayT;
typedef OneDimensionDownArrayT type[PrimaryD];
type data;
OneDimensionDownArrayTamp; operator[](unsigned i) { return data[i]; }
};

int main()
{
array<int, 2, 3>::type a4 = { { 1, 2, 3}, { 1, 2, 3} };
array<int, 2, 3> a5{ { { 1, 2, 3}, { 4, 5, 6} } };
std::cout << a5[1][2] << std::endl;

array<int, 3> a6{ {1, 2, 3} };
std::cout << a6[1] << std::endl;

array<int, 1, 2, 3> a7{ { { { 1, 2, 3}, { 4, 5, 6 } } }};
std::cout << a7[0][1][2] << std::endl;
}
 

Не могли бы вы объяснить, что именно делает этот код? Я понимаю, что рекурсия используется в той или иной форме здесь для создания многомерного массива, но я немного смущен тем, как работает этот процесс.

Меня также смущает эта строка:

 array<int, 2, 3>::type a4 = { { 1, 2, 3}, { 1, 2, 3} };
 

Что такое ::type ?

Ответ №1:

Это то, что называется специализацией шаблонов.

Первое, что вы должны знать, это то, что все, что определено с template помощью, оценивается во время компиляции.

Глядя на ваш код, мы хотим рассмотреть два основных случая:

  1. Когда массив имеет одномерность
  2. Когда массив имеет более одного измерения

Ниже описывается одномерный случай:

 template<class T, unsigned PrimaryD >
struct array<T, PrimaryD>
{
typedef T type[PrimaryD];
type data;
Tamp; operator[](unsigned i) { return data[i]; }

};
 

Как вы можете видеть, приведенная выше структура специализирована для приема только двух параметров шаблона, первый T — это тип данных массива, а второй — PrimaryD это просто означает, какой длины должен быть массив.

Теперь для 2-й специализации:

 template<class T, unsigned PrimaryD, unsigned ... RestD >
struct array<T, PrimaryD, RestD...>
{
    // if the template contains array<int, 1, 2, 3, 4>
    // then RestD... will unpack the values   2, 3, 4
    typedef typename array<T, RestD...>::type 
    OneDimensionDownArrayT;
   
    typedef OneDimensionDownArrayT type[PrimaryD];
    type data;
    
    // This will return an array type 
    // you can keep on making a call to operator[] until the type of the OneDimensionDownArrayT refers to the previous specialization
    OneDimensionDownArrayTamp; operator[](unsigned i) { return 
              data[i]; 
    }
};
 

Теперь для вашего примера:

array<int, 2, 3>::type a4 = { { 1, 2, 3}, { 1, 2, 3} }

Что будет делать компилятор, так это сопоставлять параметры шаблона, а именно, int , 2 , и 3 которые оцениваются по 2-й специализации.

Давайте проследим за этим:

array<int, 2, 3> какая структура содержит следующие конкретные типы: OneDimensionalT, который равен array<int, 3> и operator[] функция, которая возвращает array<int, 3> .

OneDimensionalT равно array<int, 3> потому что, если вы распакуете RestD... значения, которые вы получите 3 , 2 они уже используются первым параметром шаблона, PrimaryD .

Добавление 3-го измерения следует той же логике, продолжайте рекурсивно определять типы структурных массивов меньшего размера, используя только значения измерений n-1 пакета параметров RestD...

О, и кстати, строка сверху предназначена только для объявления базового типа, чтобы мы могли его специализировать.

Комментарии:

1. Спасибо за ваш ответ! Чрезвычайно краткое объяснение проблемы, с которой я долго боролся, большое вам спасибо!