用constexpr c ++ 11填充N大小的数组

时间:2018-08-08 19:54:01

标签: c++11 templates c++14 template-meta-programming constexpr

我知道我的代码应该在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();

1 个答案:

答案 0 :(得分:2)

  

我知道我的代码应在c ++ 14中工作

嗯...一点也不。

您的代码有很多问题。

其中一些,没有特别的顺序

(1)你不会写

array<Fruit*, FRUIT_AMOUNT>

因为Fruit不是类型;这是一个模板类。

例如,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_sequencestd::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替代,只能在运行时使用

#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();

 }