我有一个字符串数组,必须分配一次,并且它们的基础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和更高版本的新特性。
答案 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();
}