首页 > 解决方案 > 用 constexpr c++11 填充 N 大小的数组

问题描述

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

标签: c++11templatesc++14template-meta-programmingconstexpr

解决方案


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

注意这两个函数都是一个返回语句;这对于constexprC++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替换,仅适用于运行时

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

 }

推荐阅读