为专门化模板类添加新的构造函数



我有一个定义固定长度的数组n的类,带有一些方法。

template<int n>
struct array_container{
/* some code here */
int array[n];
};

假设我想向array_container<3>添加一个构造函数,的东西:

array_container<3>::array_container(int a0, int a1 ,int a2){
array[0] = a0; 
array[1] = a1; 
array[2] = a1; 
}

我知道有两种方法:

一种是复制泛型类的整个代码,用3替换n,并添加我的构造函数:
template<>
struct array_container<3>{
/* some code here */
int array[3];

array_container(int a0, int a1 ,int a2){
array[0] = a0; 
array[1] = a1; 
array[2] = a1; }
};

这样可以正常工作,但缺点是需要从泛型基中复制所有的代码和方法。

另一个方法是在泛型类中添加构造函数array_container(int a0, int a1, int a2);,然后定义:

template<>
array_container<3>::  array_container(int a0, int a1 ,int a2){
array[0] = a0; 
array[1] = a1; 
array[2] = a1; }

这样做的缺点是用未定义的或不正确的构造函数填充泛型基类,例如array_container<2>(int a0, int a1 ,int a2)(未定义或不正确,取决于我是否将定义添加到泛型基)。

有没有一种方法可以避免这两个陷阱?Ie。不需要为专门化复制粘贴整个泛型基代码,也不向泛型基添加不必要的构造函数?

为什么不直接使用

template <std::size_t size>
struct MyArrayWithFunctions : std::array<int, size> {
/* some functions */
};

std::array允许聚合初始化,甚至可以减少大小,因此您可以简单地写入

MyArrayWithFunctions arr{1,2,3};

你可以为你自己的类添加一个推导准则,但是为什么要重新实现array呢?

是否有避免这两个陷阱的方法?Ie。不需要为专门化复制粘贴整个泛型基代码,也不向泛型基添加不必要的构造函数?

忽略这样一个事实,正如@Goswin von Brederlow所提到的,你似乎在重新发明轮子(std::array和聚合初始化),c++ 20的require -表达式允许你在主模板中定义构造函数,这些构造函数只被限制为特定的特化。例如:

#include <type_traits>
// Helper trait for constraining a ctor to a set
// of specializations.
template <int n, int... ns> struct is_one_of {
static constexpr bool value{false};
};
template <int n, int n0, int... ns> struct is_one_of<n, n0, ns...> {
static constexpr bool value{(n == n0) || is_one_of<n, ns...>::value};
};
template <int n, int... ns>
inline constexpr bool is_one_of_v{is_one_of<n, ns...>::value};
template <int n> struct array_container {
/* some code here */
int array[n];
// Constrained to n == 3 or n == 5.
template <typename... Args>
requires(std::is_same_v<Args, int> &&...) && (sizeof...(Args) == n) &&
is_one_of_v<n, 3, 5 /*, ... */> array_container(Args... args)
: array{args...} {}
};
// Demo.
array_container<3> arr3{1, 2, 3};       // OK
array_container<4> arr4{1, 2, 3, 4};    // Error (no matching ctor)
array_container<5> arr5{1, 2, 3, 4, 5}; // OK

更简单的解决方案是使用数组(在适当的位置)来构造它。

template<int n>
struct array_container{
int array[n];

array_container(std::array<int, n> arrayIn)
{
std::copy(arrayIn.begin(), arrayIn.end(), array); 
}
};

否则,可变模板和参数解包就会混乱。

如果碰巧有c++ 17编译器,可以使用以下代码:

#include <type_traits>
// class definition
template<int n>
struct array_container {
int array[n];
template<typename... Args, typename std::enable_if_t<(std::is_same_v<int, Args> && ...) && (sizeof...(Args) == n), bool> = true>
array_container(Args... args):
array{args...}
{}
};
// user defined template deduction guide
template<typename... Args>
array_container(Args...) -> array_container<sizeof...(Args)>;

,像

一样使用
array_container<3> x {1,2,3};

或者甚至让大小从参数的数量推断出来,比如

// n deduced to 3 from the number of arguments
array_container x {1,2,3};

构造函数是一个可变模板,接受任意数量的int参数(后者由std::enable_if_t模板形参强制),并从它们初始化array成员。用户定义的推导指南可用于从传递给构造函数的参数数量中自动推导出n形参。

看godbolt直播

最新更新