С++: Могу ли я иметь нестатические шаблоны переменных-членов?

96
7

Я пытаюсь написать немного кода, который требует, чтобы у меня было много std::array в классе контейнера. Эти массивы имеют разные размеры (все подряд от 2 до 16, если это имеет значение), и есть ровно один из каждого размера. Я хочу поместить их в контейнерный класс и иметь доступ к ним с помощью шаблонов.

Это, вероятно, проще объяснить с помощью кода. Я хочу что-то вроде этого:

class ContainerClass {

public:
// I want to declare some number of arrays right here, all of different
// sizes, ranging from 2-16. I'd like to be able to access them as
// arr<2> through arr<16>.

// This code gives a compiler error, understandably.
// But this is what I'd think it'd look like.
template <size_t N> // I also need to find a way to restrict N to 2 through 16.
std::array<int, N> arr;

// An example method of how I want to be able to use this.
template <size_t N>
void printOutArr() {
for (int i = 0; i < N; i++) {
std::cout << arr<N>[i] << std::endl;
}
}
};

Я бы хотел, чтобы код расширялся, как будто в нем было всего 15 массивов, от 2 до 16. Как это, но с шаблонами:

class ContainerClass {

public:
std::array<int, 2> arr2;
std::array<int, 3> arr3;
std::array<int, 4> arr4;
std::array<int, 5> arr5;
// ... and so on.
};

Из того, что я понимаю, C++ поддерживает переменные шаблоны, но похоже, что это только для статических членов в классах. Есть ли альтернатива, которая могла бы вести себя одинаково (желательно с минимальными накладными расходами)?

Если вам нужна дополнительная информация, пожалуйста, спросите.

Заранее спасибо.

спросил(а) 2021-01-19T20:16:22+03:00 2 месяца, 3 недели назад
1
Решение
115

Могу ли я иметь нестатические шаблоны переменных-членов?

Нет.

Тем не менее, вы можете использовать шаблоны для создания списка членов, как вы описываете. Вот пример использования рекурсивного наследования:

template<class T, std::size_t base, std::size_t size>
class Stair;

template<class T, std::size_t base>
class Stair<T, base, base> {};

template<class T, std::size_t base, std::size_t size>
class Stair : Stair<T, base, size - 1> {
protected:
std::array<T, size> arr;
public:
template<std::size_t s>
std::array<T, s>& array() {
return Stair<T, base, s>::arr;
}
};

int main()
{
Stair<int, 2, 10> s;
auto& arr = s.array<9>();

ответил(а) 2021-01-19T20:16:22+03:00 2 месяца, 3 недели назад
44

Я думаю, что у меня может быть решение с использованием рекурсивных шаблонов и std :: tuple. Я скомпилировал и протестировал его с помощью gcc 7.3.0.

Это заставляет меня чувствовать себя грязным, но, похоже, это работает.

#include <iostream>
#include <array>
#include <tuple>
#include <type_traits>

// Forward declare A since there is a circular dependency between A and Arrays
template <size_t size, size_t start, typename... T>
struct A;

// If size is greater than start define a type that is an A::ArrayTuple from the
// next step down (size - 1) otherwise type is void
template <size_t size, size_t start, typename E, typename... T>
struct Arrays {
using type = typename std::conditional<(size > start),
typename A<size-1, start, E, T...>::ArrayTuple,
void
>::type;
};

// Use template recursion to begin at size and define std::array<int, size>
// to add to a tuple and continue marching backwards (size--) until size == start
// When size == start take all of the std::arrays and put them into a std::tuple
//
// A<size, start>::ArrayTuple will be a tuple of length (size - start + 1) where
// the first element is std::array<int, start>, the second element is
// std::array<int, start + 1> continuing until std::array<int, size>
template <size_t size, size_t start, typename... T>
struct A {
using Array = typename std::array<int, size>;

using ArrayTuple = typename std::conditional<(size == start),
typename std::tuple<Array, T...>,
typename Arrays<size, start, Array, T...>::type
>::type;
};

// This specialization is necessary to avoid infinite template recursion
template <size_t start, typename... T>
struct A<0, start, T...> {
using Array = void;
using ArrayTuple = void;
};

template <size_t size, size_t start = 1>
class Container {
public:
using ArrayTuple = typename A<size, start>::ArrayTuple;

// Shorthand way to the get type of the Nth element in ArrayTuple
template <size_t N>
using TupleElementType = typename
std::tuple_element<N-start, ArrayTuple>::type;

ArrayTuple arrays_;

// Returns a reference to the tuple element that has the type of std::array<int, N>
template <size_t N>
TupleElementType<N>& get_array() {
// Static assertion that the size of the array at the Nth element is equal to N
static_assert(std::tuple_size< TupleElementType<N> >::value == N);
return std::get<N-start>(arrays_);
}

// Prints all elements of the tuple element that has the type of std::array<int, N>
template <size_t N>
void print_array() {
auto& arr = get_array<N>();
std::cout << "Array Size: " << arr.size() << std::endl;
for (int i = 0; i < arr.size(); i++) {
std::cout << arr[i] << std::endl;
}
}
};

int main() {
// Create a new Container object where the arrays_ member will be
// a tuple with 15 elements:
// std::tuple< std::array<int, 2>, std::array<int, 3> ... std::array<int, 16> >
Container<16,2> ctr;

auto& arr2 = ctr.get_array<2>();

arr2[0] = 20;
arr2[1] = 21;

//ctr.print_array<1>(); // Compiler error since 1 < the ctr start (2)
ctr.print_array<2>(); // prints 20 and 21
ctr.print_array<3>(); // prints 3 zeros
ctr.print_array<16>(); // prints 16 zeros
//ctr.print_array<17>(); // Compiler error since 17 > the ctr size (16)

//int x(ctr.arrays_); // Compiler error - uncomment to see the type of ctr.arrays_

return 0;
}


Здесь вывод компилятора, если я раскомментирую эту строку, где я пытаюсь объявить int x показывая тип ctr.arrays_:

so.cpp: In function ‘int main():
so.cpp:90:22: error: cannot convert ‘Container<16, 2>::ArrayTuple {aka std::tuple<std::array<int, 2>, std::array<int, 3>, std::array<int, 4>, std::array<int, 5>, std::array<int, 6>, std::array<int, 7>, std::array<int, 8>, std::array<int, 9>, std::array<int, 10>, std::array<int, 11>, std::array<int, 12>, std::array<int, 13>, std::array<int, 14>, std::array<int, 15>, std::array<int, 16> >} to ‘int in initialization
int x(ctr.arrays_); // Compiler error - uncomment to see the type of ctr.arrays_

ответил(а) 2021-01-19T20:16:22+03:00 2 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема