初始化模板内的固定长度数组 class
Initializing a fixed length array inside a template class
我需要写一个class,它包含一个固定长度的数组(数组的长度由模板参数定义),并且必须立即初始化数组,但有一个限制,即每个成员都被初始化.
另请注意,我使用的是 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 []'
我也许可以使用初始化列表来初始化数组,但是这样我不会失去必须传递完全相同大小的数组的限制吗?对我来说真的很重要,class 中的数组在编译时完全初始化。
我也不确定,如果编译器可以推断出我传递给构造函数的未命名数组的正确类型。
编辑:忘了说,由于项目限制,我不能使用标准模板库,所以不允许 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:(也需要去掉 std::decay 部分,因为这只会持有原始类型)
还需要对象是非 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_sequence
和std::index_sequence
,你可以将它们与接收TraitStruct
s的C风格数组的构造函数和委托构造函数结合起来,并编写如下内容
#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 },
} };
}
观察到,根据您的要求(“对我来说真的很重要,class 中的数组在编译时已完全初始化”),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>);
}
我需要写一个class,它包含一个固定长度的数组(数组的长度由模板参数定义),并且必须立即初始化数组,但有一个限制,即每个成员都被初始化. 另请注意,我使用的是 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 []'
我也许可以使用初始化列表来初始化数组,但是这样我不会失去必须传递完全相同大小的数组的限制吗?对我来说真的很重要,class 中的数组在编译时完全初始化。
我也不确定,如果编译器可以推断出我传递给构造函数的未命名数组的正确类型。
编辑:忘了说,由于项目限制,我不能使用标准模板库,所以不允许 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:
#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
,你可以将它们与接收TraitStruct
s的C风格数组的构造函数和委托构造函数结合起来,并编写如下内容
#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 },
} };
}
观察到,根据您的要求(“对我来说真的很重要,class 中的数组在编译时已完全初始化”),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>);
}