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

147 4

我有一个必须分配的字符串数组,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];


};



然后在singleton方式中使用它,例如:


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和以后的新的特性。

时间: 原作者:

128 5

你可以使用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];


};



原作者:
137 3

你可以编写函数来返回std::array,而你的成员可以是const,而不是c数组使用std::array :


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,你使用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) }};


}



然后叫它:


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



原作者:
109 5

按照初始化函数进行操作:


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;


}();




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():


}



原作者:
64 0

既然你想要使用新功能,那么让我们使用range-v3(即将成为C 2a库)来编写一些非常简短的代码:


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

原作者:
...