简体   繁体   English

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

[英]Initialize array of compile time defined size as constant expression

I have an array of strings that must be allocated once and their underlying c_str must remain valid for entire duration of the program. 我有一个字符串数组,必须分配一次,它们的底层c_str必须在程序的整个持续时间内保持有效。

There's some API that provides info about some arbitrary data types. 有一些API提供有关某些任意数据类型的信息。 Could look like this: 看起来像这样:

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

The getTypeSuffix is not constexpr , so this must work at least partially in runtime. getTypeSuffix不是constexpr ,因此必须至少部分在运行时工作。

The interface that I must provide: 必须提供的界面:

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

Now my array should have following type: 现在我的数组应该有以下类型:

std::string typeNames[NUMBER_OF_TYPES];

For my purposes, it will be initialized within a wrapper class, right in the constructor: 为了我的目的,它将在一个包装类中初始化,就在构造函数中:

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];
};

This is then used in an singleton-ish kind of way, for example: 然后以单一方式使用它,例如:

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

Now what I want to improve is that I can see that the for loop in the constructor could be replaced as such: 现在我要改进的是,我可以看到构造函数中的for循环可以替换为:

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

Obviously a pseudocode, but you get the point - the array can be initialized directly, leaving the constructor without body, which is neat. 显然是伪代码,但是你得到了重点 - 数组可以直接初始化,使构造函数没有正确,这是整洁的。 It also means that the array member _names can be const , further enforcing the correct usage of this helper class. 它还意味着数组成员_names可以是const ,进一步强制正确使用此帮助程序类。

I'm quite sure there would be many other uses to filling an array by expressions in compile time, instead of having loop. 我很确定在编译时通过表达式填充数组会有很多其他用途,而不是循环。 I would even suspect that this is something that happens anyway during 03 . 我甚至怀疑这是03期间发生的事情。

Is there a way to write a C++11 style array initializer list that has flexible length and is defined by an expression? 有没有办法编写一个C ++ 11样式的数组初始化列表,它具有灵活的长度并由表达式定义? Another simple example would be: 另一个简单的例子是:

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

Again, an expression instead of a loop. 同样,表达式而不是循环。

I'm not asking this question because I was trying to drastically improve performance, but because I want to learn about new, neat, features of C++14 and later. 我不是在问这个问题,因为我试图大幅度提高性能,但是因为我想学习C ++ 14及更高版本的新的,简洁的功能。

instead of C-array use std::array , then you might write your function to return that std::array and your member can then be const : 而不是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;
};

Now you have std::array , you might use variadic template instead of loop, something like ( std::index_sequence stuff is C++14, but can be implemented in C++11): 现在你有了std::array ,你可以使用variadic模板而不是loop,比如( std::index_sequence stuff是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) }};
}

and then call it: 然后调用它:

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

You can defer to an initialization function: 您可以按照初始化函数:

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

which can be an immediately invoked lambda: 这可以是一个立即调用的lambda:

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

or do you really need the array requirement? 或者你真的需要array要求吗? We're making strings anyway so I don't understand, then you can just use range-v3: 无论如何我们正在制作字符串所以我不明白,那么你可以使用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():
}

You can use std::make_integer_sequence and a delegating constructor in C++14 (Implementations of std::make_integer_sequence exist in C++11, so this is not really C++14 specific) to get a template parameter pack of integers 您可以在C ++ 14中使用std::make_integer_sequence和委托构造函数(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];
};

This means that no strings are being default constructed. 这意味着没有默认构造的字符串。

Since you ache to use new features, let's use range-v3 (soon-to-be the ranges library in C++2a) to write some really short code: 既然你想要使用新功能,那么让我们使用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 https://godbolt.org/z/UVoENh

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM