问题描述
我想创建一个 constexpr std::array<std::string_view,ConstexprNumber>
。例如,它应该包含 constexpr std::strings_view's
,如下所示:
"text0","text1","text2",..... "textn"
我想出了以下初始解决方案:
#include <iostream>
#include <array>
#include <utility>
#include <string>
#include <string_view>
// Number of strings that we want to generate
constexpr size_t NumberOfTextsToGenerate = 10u;
// constexpr function to build a string
constexpr std::string_view makeString(unsigned int i) {
return std::string_view("text");
}
// Helper: constexpr function that will create an array of string_views and initialize it
template <unsigned int... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<unsigned int,ManyIntegers...>) {
return std::array<std::string_view,sizeof...(ManyIntegers)>{ {makeString(ManyIntegers)...}};
}
// Helper: constexpr function that will return an array of string_views as shown above with a specified number of texts
constexpr auto generateTextArray() {
return generateTextHelper(std::make_integer_sequence<unsigned int,NumberOfTextsToGenerate>());
}
// This is a deFinition of a std::array<std::string_view,UpperBound> initialized with some text
constexpr auto text = generateTextArray();
int main() {
for (size_t i{}; i < NumberOfTextsToGenerate; ++i)
std::cout << text[i] << '\n';
return 0;
}
但是,当然,“makeString”函数不会做我想要的。或者说,我不知道如何实施正确的解决方案。
我们怎样才能让这样的数组工作呢?是基于这个初始解决方案还是类似的解决方案?
解决方法
您可以执行以下操作:
constexpr size_t NumberOfTextsToGenerate = 10u;
template <std::size_t I>
struct digit_to_end_impl {
static constexpr const char value[]{ 't','e','x','t',(I + '0'),0 };
};
template <std::size_t I>
struct digit_to_end {
static constexpr std::string_view value = digit_to_end_impl<I>::value;
};
template <std::size_t I>
constexpr std::string_view makeString() {
return digit_to_end<I>::value;
}
template <unsigned int... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<unsigned int,ManyIntegers...>) {
return std::array<std::string_view,sizeof...(ManyIntegers)>{ { makeString<ManyIntegers>()... } };
}
// ...
查看DEMO。
但是,当 NumberOfTextsToGenerate
大于 10 时,这将不起作用。
哇哦,我终于做到了!!!
(链接到 godbold)
#include <iostream>
#include <array>
#include <utility>
#include <string>
#include <string_view>
template <class T>
using const_c_str_char_t = std::remove_const_t<std::remove_pointer_t<T>>;
template <auto str1,auto str2,size_t ...indexes1,size_t ...indexes2>
constexpr decltype(auto) string_append_sequence(std::index_sequence<indexes1...>,std::index_sequence<indexes2...>)
{
using char_type = const_c_str_char_t<decltype(str1())>;
static_assert(std::is_same_v<char_type,const_c_str_char_t<decltype(str2())>>);
return std::integer_sequence<char_type,str1()[indexes1]...,str2()[indexes2]...>{};
}
template <class T,T ...values1,T ...values2>
constexpr decltype(auto) append_sequence(std::integer_sequence<T,values1...>,std::integer_sequence<T,values2...>) {
return std::integer_sequence<T,values1...,values2...>{};
}
template <class sequence_t>
struct string_sequence_to_view;
template <class char_type,char_type ...chars>
struct string_sequence_to_view<std::integer_sequence<char_type,chars...>>
{
using string_view_t = std::conditional_t<std::is_same_v<char_type,char>,std::string_view,std::wstring_view>;
static constexpr decltype(auto) get() {
return string_view_t{c_str};
}
static constexpr const char_type c_str[]{chars...,char_type{}};
};
template <class char_type,size_t value,std::enable_if_t<std::is_same_v<char_type,char> || std::is_same_v<char_type,wchar_t>,int> = 0>
constexpr decltype(auto) integer_to_string_sequence()
{
constexpr auto digits = []()
{
if constexpr (std::is_same_v<char_type,char>) {
return "0123456789abcdefghijklmnopqrstuvwxyz";
}
else if constexpr (std::is_same_v<char_type,wchar_t>) {
return L"0123456789abcdefghijklmnopqrstuvwxyz";
}
};
constexpr size_t remainder = value % 10;
constexpr size_t next_value = value / 10;
if constexpr (next_value != 0) {
return append_sequence(integer_to_string_sequence<char_type,next_value>(),std::integer_sequence<char_type,digits()[remainder]>{});
}
else {
return std::integer_sequence<char_type,digits()[remainder]>{};
}
}
#define INT_TO_C_STR(char_type,num) string_sequence_to_view<decltype(integer_to_string_sequence<char_type,num>())>{}.c_str
#define APPEND_C_STR_AS_VIEW(s1,s2) \
string_sequence_to_view< \
decltype( \
string_append_sequence< \
[] { return s1; },\
[] { return s2; } \
>( \
std::make_index_sequence<sizeof(s1) / sizeof(s1[0]) - 1>(),\
std::make_index_sequence<sizeof(s2) / sizeof(s1[0]) - 1>() \
) \
) \
>{}.get()
// Number of strings that we want to generate
constexpr size_t NumberOfTextsToGenerate = 20u;
// constexpr function to build a string
template <size_t i>
constexpr std::string_view makeString() {
return APPEND_C_STR_AS_VIEW("test",INT_TO_C_STR(char,i));
}
template <size_t i>
constexpr std::wstring_view makeStringW() {
return APPEND_C_STR_AS_VIEW(L"test",INT_TO_C_STR(wchar_t,i));
}
// Helper: constexpr function that will create an array of string_views and initialize it
template <size_t... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<size_t,sizeof...(ManyIntegers)>{ makeString<ManyIntegers>()...};
}
template <size_t... ManyIntegers>
constexpr auto generateTextHelperW(std::integer_sequence<size_t,ManyIntegers...>) {
return std::array<std::wstring_view,sizeof...(ManyIntegers)>{ makeStringW<ManyIntegers>()...};
}
// Helper: constexpr function that will return an array of string_views as shown above with a specified number of texts
constexpr auto generateTextArray() {
return generateTextHelper(std::make_integer_sequence<size_t,NumberOfTextsToGenerate>());
}
constexpr auto generateTextArrayW() {
return generateTextHelperW(std::make_integer_sequence<size_t,NumberOfTextsToGenerate>());
}
// This is a definition of a std::array<std::string_view,UpperBound> initialized with some text
constexpr auto text = generateTextArray();
constexpr auto textW = generateTextArrayW();
int main()
{
for (size_t i{}; i < NumberOfTextsToGenerate; ++i) {
std::cout << text[i] << '\n';
}
for (size_t i{}; i < NumberOfTextsToGenerate; ++i) {
std::wcout << textW[i] << L'\n';
}
return 0;
}
输出:
test0
test1
test2
test3
test4
test5
test6
test7
test8
test9
test10
test11
test12
test13
test14
test15
test16
test17
test18
test19
编辑:支持 wchar_t
字符串。
我现在找到了一个最终满足我需求的解决方案。
因为 SO 用户提供了许多很好的答案以及对我的问题 here 的其他答案,
我现在将使用以下代码。
#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
#include <string>
// Specification for the output that we want to generate
constexpr const char BaseString[]{ "text" }; // Some example text
constexpr size_t StartIndex = 1u; // Start index. So first array element will be "Text1"
constexpr size_t NumberOfElements = 20u; // Number of elements to create. Last array element will be "Text20"
// These templates are used to generate a std::array
namespace ConstexprGenerator {
// To create something like "text123" as constexpr
template <const size_t numberToConvert,const char* Text>
class Converter {
public:
// Some helper variables for calculating sizes
static constexpr size_t TextLength{ std::char_traits<char>::length(Text) };
static constexpr size_t NumberOfDigits{ ([]() constexpr noexcept {size_t result = 0; int temp = numberToConvert; for (; temp != 0; temp /= 10) ++result; return result; }()) };
static constexpr size_t ArrayLength{ (numberToConvert ? 1u : 2u) + NumberOfDigits + TextLength };
// Here we will store the string
std::array<char,ArrayLength> internalString{};
// Constructor: Copy text and Convert number to character digits
constexpr Converter() noexcept {
size_t i{ 0 }; for (; i < TextLength; ++i) internalString[i] = Text[i]; // Copy text
if (numberToConvert == 0) internalString[i] = '0'; // In case that the given number is 0,then simply copy '0' character
else {
i = NumberOfDigits + TextLength - 1; // Convert number to character digits
int number = numberToConvert; for (; number; number /= 10)
internalString[i--] = number % 10 + '0';
}
}
constexpr std::array<char,ArrayLength> get() const { return *this; }; // getter
constexpr operator std::array<char,ArrayLength>() const { return internalString; } // type cast
};
// Templated variable. Will have always a different type,depending on the template parameters
template<const size_t numberToConvert,const char* Text>
constexpr auto Converted = Converter<numberToConvert,Text>{}.get();
// Generate a std::array with n elements that consist of const char *,pointing to Textx...Texty
template <int... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<size_t,ManyIntegers...>) noexcept {
return std::array<const char*,sizeof...(ManyIntegers)>{ {Converted<ManyIntegers + StartIndex,BaseString>.data()...}};
}
// Generate the required number of texts
constexpr auto generateTextArray()noexcept {
return generateTextHelper(std::make_integer_sequence<size_t,NumberOfElements>());
}
}
// This is a constexpr array
constexpr auto text = ConstexprGenerator::generateTextArray();
int main() {
std::copy(text.begin(),text.end(),std::ostream_iterator<const char*>(std::cout,"\n"));
return 0;
}
使用 MSVC、Clang 和 gcc 进行测试