用 constexpr c++11 填充 N 大小的数组
Filling N sized array with constexpr c++11
我知道我的代码应该在 c++14 中工作,但我必须在 c++11 中复制此行为,我无法做出等效的 init()
谁能帮忙?
enum MyEnum {
BANANA, APPLE, PINEAPPLE, ORANGE, FRUIT_AMOUNT
};
template<MyEnum>
struct Fruit{
virtual ~Fruit(){}
virtual void dostuff(){}
};
template <>
struct Fruit<ORANGE>{
void dostuff(){ cout<<"Hey apple!"<<endl;
}
constexpr array< Fruit*, FRUIT_AMOUNT > init(){
array< Fruit*, FRUIT_AMOUNT > myArray;
for(int i =0; i < FRUIT_AMOUNT; i++)
myArray[i] = new Fruit< (MyEnum) i >();
return myArray;
}
array<Fruit*, FRUIT_AMOUNT> myPrettyFruits = init();
I'm aware that my code should work in c++14
嗯……一点也不。
你的代码有很多问题。
其中一些,排名不分先后
(1) 你不能写
array<Fruit*, FRUIT_AMOUNT>
因为 Fruit
不是类型;这是一个模板 class。
所以,举个例子,Fruit<BANANA>
是一个类型,你可以写成
std::array<Fruit<BANANA> *, FRUIT_AMOUNT>
但是你不能有一个指向 Fruit
的指针,因为 Fruit
(没有解释模板参数)不是一个类型。
这个问题的一个可能的解决方案是使所有 Fruit
类型继承自一个公共基础;举个例子
struct FruitBase
{ };
template <MyEnum>
struct Fruit : public FruitBase
{
virtual ~Fruit () {}
virtual void dostuff () {}
};
这样你就可以得到一个 FruitBase
指针数组
std::array<FruitBase *, FRUIT_AMOUNT>
因此您可以将指向 Fruit<Something>
类型的数组指针放入也是 FruitBase
指针。
(2) 你不能
Fruit< (MyEnum) i >
其中 i
是 运行 时间已知变量,因为模板参数必须在编译时已知。
一种可能的 C++14 解决方案是使用 std::make_index_sequence
获取模板序列(编译时已知)std::size_t
值。
我建议如下
template <std::size_t ... Is>
constexpr std::array<FruitBase *, FRUIT_AMOUNT>
init_helper (std::index_sequence<Is...> const &)
{ return { { (FruitBase*)(new Fruit<(MyEnum)Is>()) ... } }; }
constexpr std::array<FruitBase *, FRUIT_AMOUNT> init ()
{ return init_helper(std::make_index_sequence<FRUIT_AMOUNT>{}); }
观察到两个函数都是单个 return 语句;这对于 constexpr
C++11 函数是必需的。
不幸的是,std::index_sequence
和 std::make_index_sequence
只能从 C++14 开始使用。但有可能用 C++11 替代它们。
(3) new Something{}
编译时无法执行
所以你可以定义 init_helper()
constexpr
但它是一个假的 constexpr
函数(所以 init()
也是一个假的 constexpr
函数)因为不能'编译时不执行。
所以你可以这样写
std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
但 myPrettyFruits
被初始化 运行 次。
如果你尝试在编译时初始化它
constexpr std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
您遇到编译错误。
以下是一个完整的编译 C++11 示例,带有 std::index_sequence
/std::make_index_sequence
替换,仅适用于 运行-time
#include <array>
#include <iostream>
template <std::size_t...>
struct indexSequence
{ using type = indexSequence; };
template <typename, typename>
struct concatSequences;
template <std::size_t... S1, std::size_t... S2>
struct concatSequences<indexSequence<S1...>, indexSequence<S2...>>
: public indexSequence<S1..., ( sizeof...(S1) + S2 )...>
{ };
template <std::size_t N>
struct makeIndexSequenceH
: public concatSequences<
typename makeIndexSequenceH<(N>>1)>::type,
typename makeIndexSequenceH<N-(N>>1)>::type>::type
{ };
template<>
struct makeIndexSequenceH<0> : public indexSequence<>
{ };
template<>
struct makeIndexSequenceH<1> : public indexSequence<0>
{ };
template <std::size_t N>
using makeIndexSequence = typename makeIndexSequenceH<N>::type;
enum MyEnum
{ BANANA, APPLE, PINEAPPLE, ORANGE, FRUIT_AMOUNT };
struct FruitBase
{ };
template <MyEnum>
struct Fruit : public FruitBase
{
virtual ~Fruit () {}
virtual void dostuff () {}
};
template <>
struct Fruit<ORANGE> : public FruitBase
{ void dostuff () { std::cout << "Hey apple!" << std::endl; } };
// fake constexpr function: new can't be executed compile-time
template <std::size_t ... Is>
constexpr std::array<FruitBase *, FRUIT_AMOUNT>
init_helper (indexSequence<Is...> const &)
{ return { { (FruitBase*)(new Fruit<(MyEnum)Is>()) ... } }; }
// fake constexpr: init_helper() can't be executed compile-time
constexpr std::array<FruitBase *, FRUIT_AMOUNT> init ()
{ return init_helper(makeIndexSequence<FRUIT_AMOUNT>{}); }
int main ()
{
// compile (executed run-time)
std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
// compilation error (init() can't be executed compile-time)
//constexpr std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
}
我知道我的代码应该在 c++14 中工作,但我必须在 c++11 中复制此行为,我无法做出等效的 init()
谁能帮忙?
enum MyEnum {
BANANA, APPLE, PINEAPPLE, ORANGE, FRUIT_AMOUNT
};
template<MyEnum>
struct Fruit{
virtual ~Fruit(){}
virtual void dostuff(){}
};
template <>
struct Fruit<ORANGE>{
void dostuff(){ cout<<"Hey apple!"<<endl;
}
constexpr array< Fruit*, FRUIT_AMOUNT > init(){
array< Fruit*, FRUIT_AMOUNT > myArray;
for(int i =0; i < FRUIT_AMOUNT; i++)
myArray[i] = new Fruit< (MyEnum) i >();
return myArray;
}
array<Fruit*, FRUIT_AMOUNT> myPrettyFruits = init();
I'm aware that my code should work in c++14
嗯……一点也不。
你的代码有很多问题。
其中一些,排名不分先后
(1) 你不能写
array<Fruit*, FRUIT_AMOUNT>
因为 Fruit
不是类型;这是一个模板 class。
所以,举个例子,Fruit<BANANA>
是一个类型,你可以写成
std::array<Fruit<BANANA> *, FRUIT_AMOUNT>
但是你不能有一个指向 Fruit
的指针,因为 Fruit
(没有解释模板参数)不是一个类型。
这个问题的一个可能的解决方案是使所有 Fruit
类型继承自一个公共基础;举个例子
struct FruitBase
{ };
template <MyEnum>
struct Fruit : public FruitBase
{
virtual ~Fruit () {}
virtual void dostuff () {}
};
这样你就可以得到一个 FruitBase
指针数组
std::array<FruitBase *, FRUIT_AMOUNT>
因此您可以将指向 Fruit<Something>
类型的数组指针放入也是 FruitBase
指针。
(2) 你不能
Fruit< (MyEnum) i >
其中 i
是 运行 时间已知变量,因为模板参数必须在编译时已知。
一种可能的 C++14 解决方案是使用 std::make_index_sequence
获取模板序列(编译时已知)std::size_t
值。
我建议如下
template <std::size_t ... Is>
constexpr std::array<FruitBase *, FRUIT_AMOUNT>
init_helper (std::index_sequence<Is...> const &)
{ return { { (FruitBase*)(new Fruit<(MyEnum)Is>()) ... } }; }
constexpr std::array<FruitBase *, FRUIT_AMOUNT> init ()
{ return init_helper(std::make_index_sequence<FRUIT_AMOUNT>{}); }
观察到两个函数都是单个 return 语句;这对于 constexpr
C++11 函数是必需的。
不幸的是,std::index_sequence
和 std::make_index_sequence
只能从 C++14 开始使用。但有可能用 C++11 替代它们。
(3) new Something{}
编译时无法执行
所以你可以定义 init_helper()
constexpr
但它是一个假的 constexpr
函数(所以 init()
也是一个假的 constexpr
函数)因为不能'编译时不执行。
所以你可以这样写
std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
但 myPrettyFruits
被初始化 运行 次。
如果你尝试在编译时初始化它
constexpr std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
您遇到编译错误。
以下是一个完整的编译 C++11 示例,带有 std::index_sequence
/std::make_index_sequence
替换,仅适用于 运行-time
#include <array>
#include <iostream>
template <std::size_t...>
struct indexSequence
{ using type = indexSequence; };
template <typename, typename>
struct concatSequences;
template <std::size_t... S1, std::size_t... S2>
struct concatSequences<indexSequence<S1...>, indexSequence<S2...>>
: public indexSequence<S1..., ( sizeof...(S1) + S2 )...>
{ };
template <std::size_t N>
struct makeIndexSequenceH
: public concatSequences<
typename makeIndexSequenceH<(N>>1)>::type,
typename makeIndexSequenceH<N-(N>>1)>::type>::type
{ };
template<>
struct makeIndexSequenceH<0> : public indexSequence<>
{ };
template<>
struct makeIndexSequenceH<1> : public indexSequence<0>
{ };
template <std::size_t N>
using makeIndexSequence = typename makeIndexSequenceH<N>::type;
enum MyEnum
{ BANANA, APPLE, PINEAPPLE, ORANGE, FRUIT_AMOUNT };
struct FruitBase
{ };
template <MyEnum>
struct Fruit : public FruitBase
{
virtual ~Fruit () {}
virtual void dostuff () {}
};
template <>
struct Fruit<ORANGE> : public FruitBase
{ void dostuff () { std::cout << "Hey apple!" << std::endl; } };
// fake constexpr function: new can't be executed compile-time
template <std::size_t ... Is>
constexpr std::array<FruitBase *, FRUIT_AMOUNT>
init_helper (indexSequence<Is...> const &)
{ return { { (FruitBase*)(new Fruit<(MyEnum)Is>()) ... } }; }
// fake constexpr: init_helper() can't be executed compile-time
constexpr std::array<FruitBase *, FRUIT_AMOUNT> init ()
{ return init_helper(makeIndexSequence<FRUIT_AMOUNT>{}); }
int main ()
{
// compile (executed run-time)
std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
// compilation error (init() can't be executed compile-time)
//constexpr std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
}