将编译时定义的大小的数组初始化为常量表达式

时间:2019-06-06 15:46:23

标签: c++ arrays c++11 c++14

我有一个字符串数组,必须分配一次,并且它们的基础c_str必须在程序的整个过程中保持有效。

有些API提供有关某些任意数据类型的信息。可能看起来像这样:

// Defined outside my code
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);

getTypeSuffix不是constexpr,因此它必须至少部分在运行时有效。

必须提供的界面:

// Returned pointer must statically allocated (not on stack, not malloc)
const char* getReadableTypeName(int type);

现在我的数组应该具有以下类型:

std::string typeNames[NUMBER_OF_TYPES];

出于我的目的,它将在包装器类中初始化,就在构造函数中:

class MyNames
{
  MyNames()
  {
    for (int i = 0; i < NUMBER_OF_TYPES; ++i)
    {
      names[i] = std::string("Type ") + getTypeSuffix(i);
    }
  }

  const char* operator[](int type) { return _names[(int)type].c_str(); }

private:
  std::string _names[NUMBER_OF_TYPES];
};

然后以单例形式使用这种方式,例如:

const char* getReadableTypeName(int type) 
{
  static MyNames names;
  return names[type];
}

现在我要改进的是,我可以看到构造函数中的for循环可以这样替换:

 MyNames() : _names{std::string("Type ") + getTypeSuffix(0), std::string("Type ") + getTypeSuffix(1), ... , std::string("Type ") + getTypeSuffix(NUMBER_OF_TYPES-1)}
 {}

显然是伪代码,但您明白了-可以直接初始化数组,而使构造函数没有主体,这很整洁。这也意味着数组成员_names可以是const,从而进一步加强了此帮助器类的正确用法。

我很确定在编译时用表达式填充数组还有很多其他用途,而不是有循环。我什至怀疑这是在03期间发生的事情。

有没有一种方法可以编写具有灵活长度并由表达式定义的C ++ 11样式数组初始化程序列表?另一个简单的例子是:

constexpr int numberCount = 10;
std::string numbers[] = {std::to_string(1), std::to_string(2), ... , std::to_string(numberCount)};

还是表达式,而不是循环。

我之所以问这个问题不是因为我试图极大地提高性能,而是因为我想了解C ++ 14和更高版本的新特性。

4 个答案:

答案 0 :(得分:6)

代替C数组使用std::array,那么您可以编写函数以返回该std::array,然后您的成员可以是const

std::array<std::string, NUMBER_OF_TYPES> build_names()
{
    std::array<std::string, NUMBER_OF_TYPES> names;
    for (int i = 0; i < NUMBER_OF_TYPES; ++i)
    {
          names[i] = std::string("Type ") + getTypeSuffix(i);
    }
    return names;
}


class MyNames
{
  MyNames() : _names(build_names()) {}
  const char* operator[](int type) const { return _names[(int)type].c_str(); }

private:
  const std::array<std::string, NUMBER_OF_TYPES> _names;
};

现在有了std::array,您可以使用可变参数模板而不是循环,例如(std::index_sequence的东西是C ++ 14,但可以在C ++ 11中实现)

template <std::size_t ... Is> 
std::array<std::string, sizeof...(Is)> build_names(std::index_sequence<Is...>)
{
     return {{ std::string("Type ") + getTypeSuffix(i) }};
}

然后命名:

MyNames() : _names(build_names(std::make_index_sequence<NUMBER_OF_TYPES>())) {}

答案 1 :(得分:3)

您可以使用初始化功能:

std::array<std::string, NUMBER_OF_TYPES> initializeNames()
{
    std::array<std::string, NUMBER_OF_TYPES> names;
    for (int i = 0; i < NUMBER_OF_TYPES; ++i) {
        names[i] = std::string("Type ") + getTypeSuffix(i);
    }
    return names;
}

const char* getReadableTypeName(int type) 
{
  static auto const names = initializeNames();
  return names[type].c_str();
}

可以是立即调用的lambda:

static auto const names = []{
    std::array<std::string, NUMBER_OF_TYPES> names;
    // ...
    return names;
}();

还是您真的需要array要求吗?无论如何我们都在制作字符串,所以我听不懂,然后您可以使用range-v3:

char const* getReadableTypeName(int type) {
    static auto const names =
        view::iota(0, NUMBER_OF_TYPES)
        | view::transform([](int i){ return "Type "s + getTypeSuffix(i); })
        | ranges::to<std::vector>();
    return names[type].c_str():
}

答案 2 :(得分:2)

您可以使用std::make_integer_sequence和C ++ 14中的委派构造函数(C ++ 11中存在std::make_integer_sequence的实现,因此这实际上不是C ++ 14特定的)来获取模板整数参数包

#include <string>
#include <utility>

#define NUMBER_OF_TYPES 23

const char* getTypeSuffix(int index);

class MyNames
{
  MyNames() : MyNames(std::make_integer_sequence<int, NUMBER_OF_TYPES>{}) {}

  template<int... Indices>
  MyNames(std::integer_sequence<int, Indices...>) : _names{ (std::string("Type ") + getTypeSuffix(Indices))... } {}

  const char* operator[](int type) { return _names[(int)type].c_str(); }

private:
  const std::string _names[NUMBER_OF_TYPES];
};

这意味着没有默认构造的字符串。

答案 3 :(得分:1)

由于您想使用新功能,因此让我们使用range-v3(在C ++ 2a中即将成为ranges库)来编写一些非常简短的代码:

const char* getReadableTypeName(int type) 
{
    static const std::vector<std::string> names =
        view::ints(0, 23) | view::transform([](int i) {
            return "Type " + std::to_string(i);
        });
    return names[type].c_str();
}

https://godbolt.org/z/UVoENh