Reputation: 33
I'm aware that my code should work in c++14 but I have to replicate this behaviour in c++11, I havent been able to make a equivalent init()
could anyone help?
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();
Upvotes: 3
Views: 130
Reputation: 66230
I'm aware that my code should work in c++14
Ehmm... not at all.
Your code has a lot of problems.
Some of them, in no particular order
(1) You can't write
array<Fruit*, FRUIT_AMOUNT>
because Fruit
isn't a type; it's a template class.
So, by example, Fruit<BANANA>
is a type, and you can write
std::array<Fruit<BANANA> *, FRUIT_AMOUNT>
but you can't have a pointer to Fruit
because Fruit
(without explicating a template argument) isn't a type.
A possible solution for this problem is make all Fruit
types inheriting from a common base; by example
struct FruitBase
{ };
template <MyEnum>
struct Fruit : public FruitBase
{
virtual ~Fruit () {}
virtual void dostuff () {}
};
This way you can have an array of FruitBase
pointers
std::array<FruitBase *, FRUIT_AMOUNT>
So you can put in the array pointer to Fruit<Something>
types that are also FruitBase
pointers.
(2) You can't have
Fruit< (MyEnum) i >
where i
is a run-time known variable, because a template argument must be known compile time.
A possible C++14 solution is use std::make_index_sequence
to get a sequence of template (so compile-time known) std::size_t
values.
I suggest something as follows
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>{}); }
Observe that both functions are a single return statement; and this is necessary for a constexpr
C++11 function.
Unfortunately std::index_sequence
and std::make_index_sequence
are available only starting from C++14. But it's possible make a C++11 substitute for they.
(3) new Something{}
can't be executed compile-time
So you can define init_helper()
constexpr
but it's a fake constexpr
function (so also init()
is a fake constexpr
function) because can't be executed compile time.
So you can write
std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
but myPrettyFruits
is initialized run-time.
If you try to initialize it compile-time
constexpr std::array<FruitBase *, FRUIT_AMOUNT> myPrettyFruits = init();
you get a compilation error.
The following is a full compiling C++11 example, with a std::index_sequence
/std::make_index_sequence
replacement, that works only run-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();
}
Upvotes: 2