在模板 class 构造函数中区分 1D 和 2D 容器(SFINAE)
Differentiate between 1D and 2D container in template class constructor (SFINAE)
所以,我有一个 class,它有一个数组数组作为私有成员。我希望每种情况都有两个构造函数(1D 或 2D)。但是当然他们的声明恰好是相同的,所以如果我不做一些事情,模板推导就无法完成它的工作。这是代码:
编辑: 我还需要它来处理 STL 容器,例如向量或 C++ 数组。这就是为什么我过于复杂而不使用 "arrays" 修复程序的原因。
#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
的内容,但我不太明白它是如何工作的。这样做可以吗?
首先,您必须 "teach" 编译器什么是二维的,什么不是。因此,您必须定义类似于以下类型特征的内容:
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 {};
然后您可以按以下方式设置 class 定义:
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); }
};
构造函数不应该相同,但您只提供了最通用的匹配。
这里不需要SFINAE。只需为一维数组提供构造函数,为二维数组提供单独的构造函数:
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++ 将使用形式为 RowsType
等的 Camel Case,然后 typedef a rows_type
用于 class。
但是请注意,rows_t
实际上不是一个类型——它是一个 值。 更好的名称应该是 NRows
。
希望对您有所帮助。
所以,我有一个 class,它有一个数组数组作为私有成员。我希望每种情况都有两个构造函数(1D 或 2D)。但是当然他们的声明恰好是相同的,所以如果我不做一些事情,模板推导就无法完成它的工作。这是代码:
编辑: 我还需要它来处理 STL 容器,例如向量或 C++ 数组。这就是为什么我过于复杂而不使用 "arrays" 修复程序的原因。
#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
的内容,但我不太明白它是如何工作的。这样做可以吗?
首先,您必须 "teach" 编译器什么是二维的,什么不是。因此,您必须定义类似于以下类型特征的内容:
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 {};
然后您可以按以下方式设置 class 定义:
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); }
};
构造函数不应该相同,但您只提供了最通用的匹配。
这里不需要SFINAE。只需为一维数组提供构造函数,为二维数组提供单独的构造函数:
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++ 将使用形式为 RowsType
等的 Camel Case,然后 typedef a rows_type
用于 class。
但是请注意,rows_t
实际上不是一个类型——它是一个 值。 更好的名称应该是 NRows
。
希望对您有所帮助。