我需要编写一个类,它包含一个固定长度的数组(由模板参数定义的数组长度),并且必须立即初始化该数组,并限制每个成员都被初始化。
另请注意,我使用的是 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::vector
或 std::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:details of std::make_index_sequence and std::index_sequence (也需要去掉 std::decay 部分,因为它只包含原始类型)
还需要对象是非常数的,因为我需要在运行时修改它(反射(reflect)在示例代码中)
#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_sequence
和 std::index_sequence
,您可以将它们与接收 C 样式数组 TraitStruct
的构造函数结合起来。 s 和委托(delegate)构造函数并编写如下内容
#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_sequence
和 std::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>);
}
关于c++ - 在模板类中初始化一个固定长度的数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59791020/