Различайте 1D и 2D контейнер в конструкторе класса шаблона (SFINAE)
Итак, у меня есть класс, который имеет массив массивов в качестве частного члена. Я хочу иметь два конструктора для каждого случая (1D или 2D). Но, конечно, их декларации совпадают, так что шаблонная дедукция не может делать свою работу без меня. Вот код:
Edit: мне также нужно, чтобы он работал с контейнерами STL, такими как vector или C++ array. Вот почему я чрезмерно усложняю и не собираюсь исправлять "массивы".
#include <iostream>
#include <array>
template<class T, std::size_t rows_t, std::size_t cols_t>
class test
{
private:
std::array<std::array<T, cols_t>, rows_t> _data;
public:
auto begin() { return this->_data.begin(); }
auto end() { return this->_data.end(); }
//CONSTRUCTOR
template<class type_t>
test(const type_t &arr)
{
std::size_t j = 0;
for (const auto &num : arr)
this->_data[0][j++] = num;
}
template<class type_t>
test(const type_t &arr)
{
std::size_t i = 0;
for (const auto &el : arr)
{
std::size_t j = 0;
for (const auto &num : el)
this->_data[i][j++] = num;
++i;
}
}
};
int main()
{
double arr[3] = { 1, 2, 3 };
double arr2[2][2] = { {1, 2}, {3, 4} };
test<double, 1, 3> obj = arr;
test<double, 2, 2> obj2 = arr2;
for (const auto &i : obj2)
{
for (const auto &j : i)
std::cout << j << " ";
std::cout << std::endl;
}
std::cin.get();
}
Примечание: я был читаю о enable_if
, но я не совсем понимаю, как это работает. Можно ли с этим справиться?
2 ответа:
Во-первых, вы должны "научить" компилятор, что 2D, а что нет. Следовательно, вы должны определить что-то вроде следующего признака типа:
template<typename T> struct is2D : public std::false_type {}; template<typename T, std::size_t N, std::size_t M> struct is2D<std::array<std::array<T, M>, N>> : std::true_type {}; template<typename T> struct is2D<std::vector<std::vector<T>>> : std::true_type {}; template<typename T, std::size_t N, std::size_t M> struct is2D<T[N][M]> : std::true_type {};
Затем вы можете настроить определение класса следующим образом:
template<class T, std::size_t rows_t, std::size_t cols_t> class test{ std::array<std::array<T, cols_t>, rows_t> _data; template<class type_t> std::enable_if_t<!is2D<type_t>::value, void> test_init(type_t const &arr) { std::size_t j = 0; for (const auto &num : arr) _data[0][j++] = num; } template<class type_t> std::enable_if_t<is2D<type_t>::value, void> test_init(type_t const &arr) { std::size_t i = 0; for(const auto &el : arr) { std::size_t j = 0; for (const auto &num : el) _data[i][j++] = num; ++i; } } public: auto &operator[](const std::size_t &i) { return this->_data[i]; } auto begin() { return this->_data.begin(); } auto end() { return this->_data.end(); } //CONSTRUCTOR template<class type_t> test(type_t const &arr) { test_init(arr); } };
ЖИВАЯ ДЕМОНСТРАЦИЯ
Конструкторы не должны быть одинаковыми, но вы предоставили только самое общее возможное соответствие.
[5] здесь SFINAE не нужен. Просто предоставьте конструктор для массива 1D и отдельный конструктор для массива 2D:template <typename T2, std::size_t N> test( const T2 (&a)[N] ) { ... } template <typename T2, std::size_t M, std::size_t N> test( const T2 (&a)[M][N] ) { ... }
Еще одно замечание: POSIX сохраняет имена типов, заканчивающиеся на "_t", поэтому обычно рекомендуется избегать их в своем собственном коде. (Отвратительно, я знаю.) Стандартный C++ будет использовать Camel Case вида:
Заметьте, однако, чтоRowsType
, etc, а затем typedef arows_type
для пользователи класса.rows_t
На самом деле не является типом-это значение . лучшим названием было бы что-то вродеNRows
.Надеюсь, это поможет.