初始化模板类中的固定长度数组

Initializing a fixed length array inside a template class

本文关键字:数组 初始化      更新时间:2023-10-16

我需要编写一个类,其中包含一个固定长度的数组(由模板参数定义的数组长度(,并且必须立即初始化该数组,但限制是每个成员都被初始化。 另请注意,我使用的是 c++17。

我不太熟悉 c++ 模板的全部功能,但我真的很想从纯 C 重新实现此功能,因为管理此数据结构的多个实例变得越来越烦人。

下面是示例代码:

#include <iostream>
enum TraitEnum
{
Trait_0,
Trait_1,
Trait_2,
Trait_3,
Trait_N,
};
template<typename TraitType, TraitType NType>
class TraitField
{
public:
struct TraitStruct
{
TraitType Trait;
bool Status;
};
TraitField(const TraitStruct TraitArray[NType]) :
traitArray{ TraitArray }
{}
private:
TraitStruct traitArray[NType];
};
int main()
{
TraitField<TraitEnum, Trait_N> myTraitField({
{ Trait_0, true },
{ Trait_1, true },
{ Trait_2, true },
{ Trait_3, true },
});
std::cout << "test" << std::endl;
return 0;
}

编译器给出以下错误:

error C2664: 'TraitField<TraitEnum,Trait_N>::TraitField(TraitField<TraitEnum,Trait_N> &&)': cannot convert argument 1 from 'initializer list' to 'const TraitField<TraitEnum,Trait_N>::TraitStruct []'

我也许可以使用初始值设定项列表初始化数组,但是我不会失去必须传递完全相同大小的数组的限制吗?对我来说,类中的数组在编译时完全初始化非常重要。

我也不确定,如果编译器可以推断出我传递给构造函数的未命名数组的正确类型。

编辑:忘了提,对于项目限制,我不能使用标准模板库,所以不允许std::vectorstd::array

EDIT2:为数组定义自定义容器类型后,它可以工作:

#include <iostream>
enum TraitEnum
{
Trait_0,
Trait_1,
Trait_2,
Trait_3,
Trait_N,
};
template<typename ElemType, size_t NElem>
struct ArrayType
{
ElemType data[NElem];
};
template<typename TraitType, TraitType NType>
class TraitField
{
public:
struct TraitStruct
{
TraitType Trait;
bool Status;
};
typedef ArrayType<TraitStruct, NType> TraitArrayType;
TraitField(const TraitArrayType &TraitArray) :
traitArray{ TraitArray }
{}
private:
TraitArrayType traitArray;
};
int main()
{
TraitField<TraitEnum, Trait_N>::TraitArrayType testArray{
{
{ Trait_0, true },
{ Trait_1, true },
{ Trait_2, true },
{ Trait_3, true },
}
};
TraitField<TraitEnum, Trait_N> myTraitField(testArray);
std::cout << "test" << std::endl;
return 0;
}

还有一个原因是,如果可能的话,我想避免声明"testArray",但是如果我直接使用未命名数组初始化对象,编译器会尝试将其直接转换为初始值设定项列表,而不是我定义的数组类型。

编辑3: 谢谢max66,您的解决方案似乎正是我想要的。 我只是做了一些修改,即需要从这里重新填充make_index_sequence和index_sequence:std::make_index_sequence 和 std::index_sequence 的详细信息(也需要剥离 std::d ecay 部分,因为这只会包含原始类型( 还需要对象是非 constexpr,因为我需要在运行时修改它(反映在示例代码中(

#include <iostream>
enum TraitEnum
{
Trait_0,
Trait_1,
Trait_2,
Trait_3,
Trait_N,
};
template<typename TraitType, TraitType NType>
class TraitField
{
public:
template <std::size_t... Ns>
struct index_sequence {};
template <std::size_t N, std::size_t... Is>
auto make_index_sequence_impl()
{
if constexpr (N == 0) // stop condition
{
return index_sequence<Is...>();
}
else // recursion
{
return make_index_sequence_impl<N - 1, N - 1, Is...>();
}
}
template <std::size_t N>
using make_index_sequence = decltype(make_index_sequence_impl<N>());
struct TraitStruct
{
TraitType Trait;
bool Status;
};
constexpr TraitField(TraitStruct const (&arr)[NType])
: TraitField{ arr, std::make_index_sequence<NType>{} }
{ }
public:
TraitStruct traitArray[NType];
template <std::size_t ... Is>
constexpr TraitField(TraitStruct const (&arr)[NType],
std::index_sequence<Is...>)
: traitArray{ arr[Is]... }
{ }
};
int main()
{
TraitField<TraitEnum, Trait_N> myTraitField{ {
{ Trait_0, true },
{ Trait_1, true },
{ Trait_2, true },
{ Trait_3, true },
} };
for (auto trait : myTraitField.traitArray)
{
std::cout << trait.Trait << " " << trait.Status << std::endl;
}
std::cout << std::endl;
myTraitField.traitArray[Trait_1].Status = false;
for (auto trait : myTraitField.traitArray)
{
std::cout << trait.Trait << " " << trait.Status << std::endl;
}
return 0;
}

如果你可以使用std::make_index_sequencestd::index_sequence,你可以将它们与接收 C 样式的TraitStruct数组的构造函数和一个委托构造函数结合起来,并编写如下内容

#include <utility>
#include <iostream>
enum TraitEnum
{ Trait_0, Trait_1, Trait_2, Trait_3, Trait_N, };
template <typename TraitType, TraitType NType>
class TraitField
{
public:
struct TraitStruct
{
TraitType Trait;
bool Status;
};
private:
TraitStruct traitArray[NType];
template <std::size_t ... Is>
constexpr TraitField (TraitStruct const (&arr)[NType],
std::index_sequence<Is...>)
: traitArray{ arr[Is]... }
{ }
public:
constexpr TraitField (TraitStruct const (&arr)[NType])
: TraitField{arr, std::make_index_sequence<NType>{}}
{ }
};
int main ()
{
constexpr TraitField<TraitEnum, Trait_N> myTraitField { {
{ Trait_0, true }, { Trait_1, true },
{ Trait_2, true }, { Trait_3, true },
} };
}

请注意,按照您的要求("对我来说非常重要,类中的数组在编译时完全初始化"(,myTraitField被声明为constexpr,因此它是初始化编译时(在您的"EDIT2"示例中并非如此(。

--编辑--

如果你需要一些东西来代替std::index_sequencestd::make_index_sequence,鉴于你也可以使用C++17,所以也可以if constexpr,我建议以下对数版本

#include <utility>
#include <type_traits>
template <std::size_t ...>
struct my_index_sequence
{ };
template <typename, typename>
struct append_sequences;
template <std::size_t ... Is1, std::size_t ... Is2>
struct append_sequences<my_index_sequence<Is1...>,
my_index_sequence<Is2...>>
{ using type = my_index_sequence<Is1..., sizeof...(Is1)+Is2...>; };
template <std::size_t N>
auto mmis_helper ()
{
if constexpr ( 0u == N )
return my_index_sequence<>{}; 
else if constexpr ( 1u == N )
return my_index_sequence<0u>{}; 
else
return typename append_sequences<
decltype(mmis_helper<(N >> 1)>()),
decltype(mmis_helper<N - (N >> 1)>())>::type {};
}
template <std::size_t N>
using my_make_index_sequence = decltype(mmis_helper<N>());
int main ()
{
using T1 = my_make_index_sequence<13u>;
using T2 = my_index_sequence<0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u,
10u, 11u, 12u>;
static_assert(std::is_same_v<T1, T2>);
}