C++에서 컴파일 타임 문자열을 편리하게 선언합니다.
C++에서 컴파일 시간 동안 문자열을 만들고 조작할 수 있는 것은 몇 가지 유용한 응용 프로그램이 있습니다.C++에서 컴파일 타임 문자열을 만드는 것은 가능하지만, 문자열을 다양한 문자 시퀀스로 선언해야 하기 때문에 프로세스가 매우 번거롭습니다.
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
문자열 연결, 하위 문자열 추출 등의 작업은 문자 시퀀스에 대한 작업으로 쉽게 구현할 수 있습니다.컴파일 시간 문자열을 더 편리하게 선언할 수 있습니까? 그렇지 않다면 컴파일 시간 문자열을 편리하게 선언할 수 있는 제안이 작업에 있습니까?
기존 접근 방식이 실패하는 이유
이상적으로는 컴파일 시간 문자열을 다음과 같이 선언할 수 있습니다.
// Approach 1
using str1 = sequence<"Hello, world!">;
또는 사용자 정의 리터럴을 사용하여
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
decltype(str2)
을 가질 것입니다.constexpr
例자시 과 같은수 .다음과 같은 작업을 수행할 수 있다는 점을 활용하여 접근법 1의 메시에 버전을 구현할 수 있습니다.
template <unsigned Size, const char Array[Size]>
struct foo;
그러나 어레이에는 외부 링크가 필요하므로 접근 방식 1을 사용하려면 다음과 같은 내용을 작성해야 합니다.
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
말할 필요도 없이, 이것은 매우 불편합니다.접근법 2는 실제로 구현할 수 없습니다.만약우리선다면한언가,▁a면)를 선언한다면,constexpr
연산자, 유형을 리터럴 연산자, 그렇다면 반환 유형을 어떻게 지정합니까?연산자가 다양한 문자 시퀀스를 반환해야 하기 때문에 다음을 사용해야 합니다.const char*
반환 유형을 지정하는 매개 변수:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
오류가 하는데, 는 이은컴야다면니, 하왜냐것 때문입니다.s
가 아닙니다.constexpr
다음을 수행하여 이 문제를 해결하려고 하는 것은 큰 도움이 되지 않습니다.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
표준은 이 특정 리터럴 연산자 양식이 정수 및 부동 소수점 유형에 대해 예약되도록 지정합니다.하는 동안에123_s
효과가 있을 겁니다abc_s
하지 않을 것입니다. 정의 아예 버리고 요?constexpr
기능?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
이전과 마찬가지로, 우리는 어레이가 이제 다음과 같은 매개 변수가 되는 문제에 대한 매개 변수입니다.constexpr
함수, 그 자체는 더 이상 a가 아닙니다.constexpr
활자를
하는 C 할 수 합니다.BOOST_PP_FOR
문자열화, 배열 첨자 등).는 그런 충분한 관심. ) 하지그또실시이간행할를없습크다로관니런충심만매한분는▁however =)
C++ 나우 2012에서 스콧 슈어가 선보인 우아함에 걸맞는 것을 본 적이 없습니다.필요합니다.constexpr
그래도.
사용 방법과 기능은 다음과 같습니다.
int
main()
{
constexpr str_const my_string = "Hello, world!";
static_assert(my_string.size() == 13, "");
static_assert(my_string[4] == 'o', "");
constexpr str_const my_other_string = my_string;
static_assert(my_string == my_other_string, "");
constexpr str_const world(my_string, 7, 5);
static_assert(world == "world", "");
// constexpr char x = world[5]; // Does not compile because index is out of range!
}
컴파일 시간 범위 검사보다 훨씬 시원하지 않습니다!
사용과 구현 모두 매크로가 없습니다.그리고 끈 크기에 인위적인 제한은 없습니다.저는 구현을 여기에 게시하고 싶지만, 저는 스콧의 암묵적인 저작권을 존중합니다.구현은 위에 링크된 프레젠테이션의 단일 슬라이드에 있습니다.
C++17 업데이트
가 이로 몇 년, 제가이답올몇년동안이후로린변을▁in,.std::string_view
도구 상자의 일부가 되었습니다.다음을 사용하여 위의 내용을 다시 작성하는 방법은 다음과 같습니다.string_view
:
#include <string_view>
int
main()
{
constexpr std::string_view my_string = "Hello, world!";
static_assert(my_string.size() == 13);
static_assert(my_string[4] == 'o');
constexpr std::string_view my_other_string = my_string;
static_assert(my_string == my_other_string);
constexpr std::string_view world(my_string.substr(7, 5));
static_assert(world == "world");
// constexpr char x = world.at(5); // Does not compile because index is out of range!
}
문자열과 문자열의 크기를 인수로 사용하고 문자열의 문자로 구성된 시퀀스(BOOST_PP_FOR, 문자열화, 배열 첨자 등 사용)를 반환하는 C 프리프로세서 매크로를 정의할 수 있어야 한다고 생각합니다.하지만, 저는 그러한 매크로를 실행할 시간(또는 충분한 관심)이 없습니다.
매우 간단한 매크로와 일부 C++11 기능을 사용하여 부스트에 의존하지 않고 이를 구현할 수 있습니다.
- 람다 바리케이드
- 템플릿
- 일반화된 상수식
- 정적이 아닌 데이터 멤버 초기화자
- 동일한 초기화
(뒤의 두 가지는 여기서 엄격하게 요구되는 것은 아닙니다.
0에서 N까지 사용자가 제공한 지표로 변수 템플릿을 인스턴스화할 수 있어야 합니다. 예를 들어 변수 템플릿 함수의 인수로 튜플을 확장하는 데 유용한 도구입니다(질문 참조:튜플을 변수 템플릿 함수의 인수로 확장하려면 어떻게 해야 합니까?
일치하는 함수 포인터를 호출하기 위해 튜플 "제거")namespace variadic_toolbox { template<unsigned count, template<unsigned...> class meta_functor, unsigned... indices> struct apply_range { typedef typename apply_range<count-1, meta_functor, count-1, indices...>::result result; }; template<template<unsigned...> class meta_functor, unsigned... indices> struct apply_range<0, meta_functor, indices...> { typedef typename meta_functor<indices...>::result result; }; }
그런 다음 형식이 아닌 매개 변수 char를 사용하여 string이라는 변수 템플릿을 정의합니다.
namespace compile_time { template<char... str> struct string { static constexpr const char chars[sizeof...(str)+1] = {str..., '\0'}; }; template<char... str> constexpr const char string<str...>::chars[sizeof...(str)+1]; }
이제 가장 흥미로운 부분 - 문자열 템플릿에 문자 리터럴을 전달합니다.
namespace compile_time { template<typename lambda_str_type> struct string_builder { template<unsigned... indices> struct produce { typedef string<lambda_str_type{}.chars[indices]...> result; }; }; } #define CSTRING(string_literal) \ []{ \ struct constexpr_string_type { const char * chars = string_literal; }; \ return variadic_toolbox::apply_range<sizeof(string_literal)-1, \ compile_time::string_builder<constexpr_string_type>::produce>::result{}; \ }()
간단한 연결 시연을 통해 사용 방법을 확인할 수 있습니다.
namespace compile_time
{
template<char... str0, char... str1>
string<str0..., str1...> operator*(string<str0...>, string<str1...>)
{
return {};
}
}
int main()
{
auto str0 = CSTRING("hello");
auto str1 = CSTRING(" world");
std::cout << "runtime concat: " << str_hello.chars << str_world.chars << "\n <=> \n";
std::cout << "compile concat: " << (str_hello * str_world).chars << std::endl;
}
편집: Howard Hinnant(그리고 OP에 대한 나의 논평에서 어느 정도)가 지적했듯이, 문자열의 모든 단일 문자를 단일 템플릿 인수로 포함하는 유형이 필요하지 않을 수 있습니다.이것이 필요하다면 아래에 매크로가 없는 솔루션이 있습니다.
컴파일할 때 문자열 작업을 시도하다가 발견한 트릭이 있습니다.템플릿 문자열 이외의 다른 유형을 도입해야 하지만 함수 내에서는 이 유형의 범위를 제한할 수 있습니다.
매크로를 사용하지 않고 일부 C++11 기능을 사용합니다.
#include <iostream>
// helper function
constexpr unsigned c_strlen( char const* str, unsigned count = 0 )
{
return ('\0' == str[0]) ? count : c_strlen(str+1, count+1);
}
// destination "template string" type
template < char... chars >
struct exploded_string
{
static void print()
{
char const str[] = { chars... };
std::cout.write(str, sizeof(str));
}
};
// struct to explode a `char const*` to an `exploded_string` type
template < typename StrProvider, unsigned len, char... chars >
struct explode_impl
{
using result =
typename explode_impl < StrProvider, len-1,
StrProvider::str()[len-1],
chars... > :: result;
};
// recursion end
template < typename StrProvider, char... chars >
struct explode_impl < StrProvider, 0, chars... >
{
using result = exploded_string < chars... >;
};
// syntactical sugar
template < typename StrProvider >
using explode =
typename explode_impl < StrProvider,
c_strlen(StrProvider::str()) > :: result;
int main()
{
// the trick is to introduce a type which provides the string, rather than
// storing the string itself
struct my_str_provider
{
constexpr static char const* str() { return "hello world"; }
};
auto my_str = explode < my_str_provider >{}; // as a variable
using My_Str = explode < my_str_provider >; // as a type
my_str.print();
}
Boost 솔루션을 사용하지 않으려면 비슷한 작업을 수행하는 간단한 매크로를 만들 수 있습니다.
#define MACRO_GET_1(str, i) \
(sizeof(str) > (i) ? str[(i)] : 0)
#define MACRO_GET_4(str, i) \
MACRO_GET_1(str, i+0), \
MACRO_GET_1(str, i+1), \
MACRO_GET_1(str, i+2), \
MACRO_GET_1(str, i+3)
#define MACRO_GET_16(str, i) \
MACRO_GET_4(str, i+0), \
MACRO_GET_4(str, i+4), \
MACRO_GET_4(str, i+8), \
MACRO_GET_4(str, i+12)
#define MACRO_GET_64(str, i) \
MACRO_GET_16(str, i+0), \
MACRO_GET_16(str, i+16), \
MACRO_GET_16(str, i+32), \
MACRO_GET_16(str, i+48)
#define MACRO_GET_STR(str) MACRO_GET_64(str, 0), 0 //guard for longer strings
using seq = sequence<MACRO_GET_STR("Hello world!")>;
유일한 문제는 64자의 고정 크기(추가 0)입니다.하지만 필요에 따라 쉽게 변경할 수 있습니다.
문자열과 문자열의 크기를 인수로 사용하고 문자열의 문자로 구성된 시퀀스를 반환하는 C 프리프로세서 매크로를 정의할 수 있어야 한다고 생각합니다(BOOST_PP_FOR, 문자열화, 배열 첨자 등 사용).
다음 기사가 있습니다.C++ 템플릿의 문자열을 사용하는 것은 Abel Sinkovics와 Dave Abrahams의 메타 프로그램입니다.
매크로 + BOOST_PP_REEPAT를 사용하는 아이디어보다 약간 개선되었습니다. 매크로에 명시적인 크기를 전달할 필요가 없습니다.간단히 말해서, 문자열 크기에 대한 고정 상한과 "문자열 오버런 방지"를 기반으로 합니다.
template <int N>
constexpr char at(char const(&s)[N], int i)
{
return i >= N ? '\0' : s[i];
}
추가 조건부 부스트::mpl::slot_back.
Yankes의 솔루션이 이 특정 문제를 해결하고 constexpr 또는 복잡한 전처리기 코드를 사용하지 않고도 우아하게 수행되기 때문에 저는 Yankes의 솔루션에 대한 수락된 답변을 변경했습니다.
만약 당신이 확장된 매크로에서 후행 0, 손으로 쓴 매크로 루프, 2배의 문자열 반복을 받아들이고 Boost가 없다면 - 동의합니다 - 더 좋습니다.그러나 Boost를 사용하면 다음과 같은 세 줄에 불과합니다.
#include <boost/preprocessor/repetition/repeat.hpp>
#define GET_STR_AUX(_, i, str) (sizeof(str) > (i) ? str[(i)] : 0),
#define GET_STR(str) BOOST_PP_REPEAT(64,GET_STR_AUX,str) 0
다음은 std::tuplle<char...를 생성하는 간단한 C++14 솔루션입니다.전달된 각 컴파일 시간 문자열에 대해 >.
#include <tuple>
#include <utility>
namespace detail {
template <std::size_t ... indices>
decltype(auto) build_string(const char * str, std::index_sequence<indices...>) {
return std::make_tuple(str[indices]...);
}
}
template <std::size_t N>
constexpr decltype(auto) make_string(const char(&str)[N]) {
return detail::build_string(str, std::make_index_sequence<N>());
}
auto HelloStrObject = make_string("hello");
그리고 여기 다른 매크로 게시물에서 잘라낸 고유한 컴파일 시간 유형을 만들기 위한 것이 있습니다.
#include <utility>
template <char ... Chars>
struct String {};
template <typename Str, std::size_t ... indices>
decltype(auto) build_string(std::index_sequence<indices...>) {
return String<Str().chars[indices]...>();
}
#define make_string(str) []{\
struct Str { const char * chars = str; };\
return build_string<Str>(std::make_index_sequence<sizeof(str)>());\
}()
auto HelloStrObject = make_string("hello");
사용자 정의 리터럴을 아직 사용할 수 없다는 것은 정말 유감입니다.
동료가 컴파일 시간에 메모리에 문자열을 연결하도록 제게 도전했습니다.컴파일 시 개별 문자열 인스턴스화도 포함됩니다.전체 코드 목록은 다음과 같습니다.
//Arrange strings contiguously in memory at compile-time from string literals.
//All free functions prefixed with "my" to faciliate grepping the symbol tree
//(none of them should show up).
#include <iostream>
using std::size_t;
//wrapper for const char* to "allocate" space for it at compile-time
template<size_t N>
struct String {
//C arrays can only be initialised with a comma-delimited list
//of values in curly braces. Good thing the compiler expands
//parameter packs into comma-delimited lists. Now we just have
//to get a parameter pack of char into the constructor.
template<typename... Args>
constexpr String(Args... args):_str{ args... } { }
const char _str[N];
};
//takes variadic number of chars, creates String object from it.
//i.e. myMakeStringFromChars('f', 'o', 'o', '\0') -> String<4>::_str = "foo"
template<typename... Args>
constexpr auto myMakeStringFromChars(Args... args) -> String<sizeof...(Args)> {
return String<sizeof...(args)>(args...);
}
//This struct is here just because the iteration is going up instead of
//down. The solution was to mix traditional template metaprogramming
//with constexpr to be able to terminate the recursion since the template
//parameter N is needed in order to return the right-sized String<N>.
//This class exists only to dispatch on the recursion being finished or not.
//The default below continues recursion.
template<bool TERMINATE>
struct RecurseOrStop {
template<size_t N, size_t I, typename... Args>
static constexpr String<N> recurseOrStop(const char* str, Args... args);
};
//Specialisation to terminate recursion when all characters have been
//stripped from the string and converted to a variadic template parameter pack.
template<>
struct RecurseOrStop<true> {
template<size_t N, size_t I, typename... Args>
static constexpr String<N> recurseOrStop(const char* str, Args... args);
};
//Actual function to recurse over the string and turn it into a variadic
//parameter list of characters.
//Named differently to avoid infinite recursion.
template<size_t N, size_t I = 0, typename... Args>
constexpr String<N> myRecurseOrStop(const char* str, Args... args) {
//template needed after :: since the compiler needs to distinguish
//between recurseOrStop being a function template with 2 paramaters
//or an enum being compared to N (recurseOrStop < N)
return RecurseOrStop<I == N>::template recurseOrStop<N, I>(str, args...);
}
//implementation of the declaration above
//add a character to the end of the parameter pack and recurse to next character.
template<bool TERMINATE>
template<size_t N, size_t I, typename... Args>
constexpr String<N> RecurseOrStop<TERMINATE>::recurseOrStop(const char* str,
Args... args) {
return myRecurseOrStop<N, I + 1>(str, args..., str[I]);
}
//implementation of the declaration above
//terminate recursion and construct string from full list of characters.
template<size_t N, size_t I, typename... Args>
constexpr String<N> RecurseOrStop<true>::recurseOrStop(const char* str,
Args... args) {
return myMakeStringFromChars(args...);
}
//takes a compile-time static string literal and returns String<N> from it
//this happens by transforming the string literal into a variadic paramater
//pack of char.
//i.e. myMakeString("foo") -> calls myMakeStringFromChars('f', 'o', 'o', '\0');
template<size_t N>
constexpr String<N> myMakeString(const char (&str)[N]) {
return myRecurseOrStop<N>(str);
}
//Simple tuple implementation. The only reason std::tuple isn't being used
//is because its only constexpr constructor is the default constructor.
//We need a constexpr constructor to be able to do compile-time shenanigans,
//and it's easier to roll our own tuple than to edit the standard library code.
//use MyTupleLeaf to construct MyTuple and make sure the order in memory
//is the same as the order of the variadic parameter pack passed to MyTuple.
template<typename T>
struct MyTupleLeaf {
constexpr MyTupleLeaf(T value):_value(value) { }
T _value;
};
//Use MyTupleLeaf implementation to define MyTuple.
//Won't work if used with 2 String<> objects of the same size but this
//is just a toy implementation anyway. Multiple inheritance guarantees
//data in the same order in memory as the variadic parameters.
template<typename... Args>
struct MyTuple: public MyTupleLeaf<Args>... {
constexpr MyTuple(Args... args):MyTupleLeaf<Args>(args)... { }
};
//Helper function akin to std::make_tuple. Needed since functions can deduce
//types from parameter values, but classes can't.
template<typename... Args>
constexpr MyTuple<Args...> myMakeTuple(Args... args) {
return MyTuple<Args...>(args...);
}
//Takes a variadic list of string literals and returns a tuple of String<> objects.
//These will be contiguous in memory. Trailing '\0' adds 1 to the size of each string.
//i.e. ("foo", "foobar") -> (const char (&arg1)[4], const char (&arg2)[7]) params ->
// -> MyTuple<String<4>, String<7>> return value
template<size_t... Sizes>
constexpr auto myMakeStrings(const char (&...args)[Sizes]) -> MyTuple<String<Sizes>...> {
//expands into myMakeTuple(myMakeString(arg1), myMakeString(arg2), ...)
return myMakeTuple(myMakeString(args)...);
}
//Prints tuple of strings
template<typename T> //just to avoid typing the tuple type of the strings param
void printStrings(const T& strings) {
//No std::get or any other helpers for MyTuple, so intead just cast it to
//const char* to explore its layout in memory. We could add iterators to
//myTuple and do "for(auto data: strings)" for ease of use, but the whole
//point of this exercise is the memory layout and nothing makes that clearer
//than the ugly cast below.
const char* const chars = reinterpret_cast<const char*>(&strings);
std::cout << "Printing strings of total size " << sizeof(strings);
std::cout << " bytes:\n";
std::cout << "-------------------------------\n";
for(size_t i = 0; i < sizeof(strings); ++i) {
chars[i] == '\0' ? std::cout << "\n" : std::cout << chars[i];
}
std::cout << "-------------------------------\n";
std::cout << "\n\n";
}
int main() {
{
constexpr auto strings = myMakeStrings("foo", "foobar",
"strings at compile time");
printStrings(strings);
}
{
constexpr auto strings = myMakeStrings("Some more strings",
"just to show Jeff to not try",
"to challenge C++11 again :P",
"with more",
"to show this is variadic");
printStrings(strings);
}
std::cout << "Running 'objdump -t |grep my' should show that none of the\n";
std::cout << "functions defined in this file (except printStrings()) are in\n";
std::cout << "the executable. All computations are done by the compiler at\n";
std::cout << "compile-time. printStrings() executes at run-time.\n";
}
아무도 제 다른 대답을 좋아하지 않는 것 같아요 :-<.str_const를 실제 유형으로 변환하는 방법을 보여줍니다.
#include <iostream>
#include <utility>
// constexpr string with const member functions
class str_const {
private:
const char* const p_;
const std::size_t sz_;
public:
template<std::size_t N>
constexpr str_const(const char(&a)[N]) : // ctor
p_(a), sz_(N-1) {}
constexpr char operator[](std::size_t n) const {
return n < sz_ ? p_[n] :
throw std::out_of_range("");
}
constexpr std::size_t size() const { return sz_; } // size()
};
template <char... letters>
struct string_t{
static char const * c_str() {
static constexpr char string[]={letters...,'\0'};
return string;
}
};
template<str_const const& str,std::size_t... I>
auto constexpr expand(std::index_sequence<I...>){
return string_t<str[I]...>{};
}
template<str_const const& str>
using string_const_to_type = decltype(expand<str>(std::make_index_sequence<str.size()>{}));
constexpr str_const hello{"Hello World"};
using hello_t = string_const_to_type<hello>;
int main()
{
// char c = hello_t{}; // Compile error to print type
std::cout << hello_t::c_str();
return 0;
}
clang++ -stdlib=libc++ -std=c++14(clang 3.7)로 컴파일
당신의 접근법 #1이 정답입니다.
그러나 어레이에는 외부 링크가 있어야 하므로 1에 접근하기 위해 다음과 같은 것을 작성해야 합니다: constexpr const charr[] = "안녕, 세상!";
아니요, 정확하지 않습니다.이것은 clang과 gcc로 컴파일됩니다.나는 그것이 표준 c++11이기를 바라지만, 나는 언어 레이어링이 아닙니다.
#include <iostream>
template <char... letters>
struct string_t{
static char const * c_str() {
static constexpr char string[]={letters...,'\0'};
return string;
}
};
// just live with it, but only once
using Hello_World_t = string_t<'H','e','l','l','o',' ','w','o','r','l','d','!'>;
template <typename Name>
void print()
{
//String as template parameter
std::cout << Name::c_str();
}
int main() {
std::cout << Hello_World_t::c_str() << std::endl;
print<Hello_World_t>();
return 0;
}
제가 c++17에 대해 정말로 사랑하고 싶은 것은 다음과 같을 것입니다 (완전한 접근법 #1).
// for template <char...>
<"Text"> == <'T','e','x','t'>
void-pinter가 언급한 것처럼 템플릿화된 사용자 정의 리터럴에 대한 표준에는 이미 매우 유사한 것이 존재하지만 숫자에 대해서만 존재합니다.그 때까지 다른 작은 속임수는 덮어쓰기 편집 모드 + 복사하여 붙여넣기를 사용하는 것입니다.
string_t<' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '>;
매크로가 문제가 되지 않는다면, 이것이 작동합니다(Yankes 답변에서 약간 수정됨).
#define MACRO_GET_1(str, i) \
(sizeof(str) > (i) ? str[(i)] : 0)
#define MACRO_GET_4(str, i) \
MACRO_GET_1(str, i+0), \
MACRO_GET_1(str, i+1), \
MACRO_GET_1(str, i+2), \
MACRO_GET_1(str, i+3)
#define MACRO_GET_16(str, i) \
MACRO_GET_4(str, i+0), \
MACRO_GET_4(str, i+4), \
MACRO_GET_4(str, i+8), \
MACRO_GET_4(str, i+12)
#define MACRO_GET_64(str, i) \
MACRO_GET_16(str, i+0), \
MACRO_GET_16(str, i+16), \
MACRO_GET_16(str, i+32), \
MACRO_GET_16(str, i+48)
//CT_STR means Compile-Time_String
#define CT_STR(str) string_t<MACRO_GET_64(#str, 0), 0 >//guard for longer strings
print<CT_STR(Hello World!)>();
고유한 컴파일 시간 유형을 만들기 위한 케이시의 솔루션은 약간의 수정을 거쳐 C++11에도 사용될 수 있습니다.
template <char... Chars>
struct string_t {};
namespace detail {
template <typename Str,unsigned int N,char... Chars>
struct make_string_t : make_string_t<Str,N-1,Str().chars[N-1],Chars...> {};
template <typename Str,char... Chars>
struct make_string_t<Str,0,Chars...> { typedef string_t<Chars...> type; };
} // namespace detail
#define CSTR(str) []{ \
struct Str { const char *chars = str; }; \
return detail::make_string_t<Str,sizeof(str)>::type(); \
}()
사용:
template <typename String>
void test(String) {
// ... String = string_t<'H','e','l','l','o','\0'>
}
test(CSTR("Hello"));
boost hana map을 가지고 놀다가 우연히 이 스레드를 발견했습니다.어떤 답변도 제 문제를 해결하지 못했기 때문에, 저는 다른 사람들에게 도움이 될 수도 있기 때문에 여기에 추가하고 싶은 다른 해결책을 찾았습니다.
문제는 하나 문자열과 함께 부스트 하나 맵을 사용할 때 컴파일러가 여전히 일부 런타임 코드를 생성했다는 것입니다(아래 참조).그 이유는 분명히 컴파일 시간에 지도를 쿼리하려면 다음과 같아야 한다는 것이었습니다.constexpr
은 가지않하다니습능은이로 BOOST_HANA_STRING
하는데, 는 매로는생성다니합에 수 . 이는 에서 사용할 수 없습니다.constexpr
맵은 종류의 을 가진 문자열이 합니다.반면에, 맵은 서로 다른 유형의 내용을 가진 문자열이 필요합니다.
이 스레드의 솔루션은 람다를 사용하거나 다른 컨텐츠에 대해 다른 유형을 제공하지 않기 때문에 다음과 같은 접근 방식이 유용하다는 것을 알게 되었습니다.또한 그것은 해킹을 피합니다.str<'a', 'b', 'c'>
기본적인 아이디어는 스콧 슈어의 버전을 갖는 것입니다.str_const
문자 해시에 템플릿을 사용합니다.그렇다.c++14
,그렇지만c++11
의 재귀적 구현으로 가능해야 합니다.crc32
함수(여기 참조).
// str_const from https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf?raw=true
#include <string>
template<unsigned Hash> ////// <- This is the difference...
class str_const2 { // constexpr string
private:
const char* const p_;
const std::size_t sz_;
public:
template<std::size_t N>
constexpr str_const2(const char(&a)[N]) : // ctor
p_(a), sz_(N - 1) {}
constexpr char operator[](std::size_t n) const { // []
return n < sz_ ? p_[n] :
throw std::out_of_range("");
}
constexpr std::size_t size() const { return sz_; } // size()
constexpr const char* const data() const {
return p_;
}
};
// Crc32 hash function. Non-recursive version of https://stackoverflow.com/a/23683218/8494588
static constexpr unsigned int crc_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
template<size_t N>
constexpr auto crc32(const char(&str)[N])
{
unsigned int prev_crc = 0xFFFFFFFF;
for (auto idx = 0; idx < sizeof(str) - 1; ++idx)
prev_crc = (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF];
return prev_crc ^ 0xFFFFFFFF;
}
// Conveniently create a str_const2
#define CSTRING(text) str_const2 < crc32( text ) >( text )
// Conveniently create a hana type_c<str_const2> for use in map
#define CSTRING_TYPE(text) hana::type_c<decltype(str_const2 < crc32( text ) >( text ))>
용도:
#include <boost/hana.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;
int main() {
constexpr auto s2 = CSTRING("blah");
constexpr auto X = hana::make_map(
hana::make_pair(CSTRING_TYPE("aa"), 1)
);
constexpr auto X2 = hana::insert(X, hana::make_pair(CSTRING_TYPE("aab"), 2));
constexpr auto ret = X2[(CSTRING_TYPE("aab"))];
return ret;
}
다음과 같이 과어셈러코가 있는 됩니다.clang-cl
5.0은 다음과 같습니다.
012A1370 mov eax,2
012A1375 ret
도우미 매크로 기능이 있는 C++17에서는 컴파일 타임 문자열을 쉽게 만들 수 있습니다.
template <char... Cs>
struct ConstexprString
{
static constexpr int size = sizeof...( Cs );
static constexpr char buffer[size] = { Cs... };
};
template <char... C1, char... C2>
constexpr bool operator==( const ConstexprString<C1...>& lhs, const ConstexprString<C2...>& rhs )
{
if( lhs.size != rhs.size )
return false;
return std::is_same_v<std::integer_sequence<char, C1...>, std::integer_sequence<char, C2...>>;
}
template <typename F, std::size_t... Is>
constexpr auto ConstexprStringBuilder( F f, std::index_sequence<Is...> )
{
return ConstexprString<f( Is )...>{};
}
#define CONSTEXPR_STRING( x ) \
ConstexprStringBuilder( []( std::size_t i ) constexpr { return x[i]; }, \
std::make_index_sequence<sizeof(x)>{} )
다음은 사용 예입니다.
auto n = CONSTEXPR_STRING( "ab" );
auto m = CONSTEXPR_STRING( "ab" );
static_assert(n == m);
Howard Hinnant의 아이디어를 기반으로 두 리터럴을 함께 추가하는 리터럴 클래스를 만들 수 있습니다.
template<int>
using charDummy = char;
template<int... dummy>
struct F
{
const char table[sizeof...(dummy) + 1];
constexpr F(const char* a) : table{ str_at<dummy>(a)..., 0}
{
}
constexpr F(charDummy<dummy>... a) : table{ a..., 0}
{
}
constexpr F(const F& a) : table{ a.table[dummy]..., 0}
{
}
template<int... dummyB>
constexpr F<dummy..., sizeof...(dummy)+dummyB...> operator+(F<dummyB...> b)
{
return { this->table[dummy]..., b.table[dummyB]... };
}
};
template<int I>
struct get_string
{
constexpr static auto g(const char* a) -> decltype( get_string<I-1>::g(a) + F<0>(a + I))
{
return get_string<I-1>::g(a) + F<0>(a + I);
}
};
template<>
struct get_string<0>
{
constexpr static F<0> g(const char* a)
{
return {a};
}
};
template<int I>
constexpr auto make_string(const char (&a)[I]) -> decltype( get_string<I-2>::g(a) )
{
return get_string<I-2>::g(a);
}
constexpr auto a = make_string("abc");
constexpr auto b = a+ make_string("def"); // b.table == "abcdef"
@user1115339의 답변에 아주 작은 두 가지 개선 사항을 추가하고 싶습니다.답변에 대한 댓글에 언급했지만, 편의를 위해 복사 붙여넣기 솔루션을 여기에 넣겠습니다.
은 유한차은점이입니다.FIXED_CSTRING
매크로 - 클래스 템플릿 내에서 문자열을 사용하고 인덱스 연산자에 대한 인수로 사용할 수 있습니다(예: 컴파일 타임 맵이 있는 경우).
실제 사례.
namespace variadic_toolbox
{
template<unsigned count,
template<unsigned...> class meta_functor, unsigned... indices>
struct apply_range
{
typedef typename apply_range<count-1, meta_functor, count-1, indices...>::result result;
};
template<template<unsigned...> class meta_functor, unsigned... indices>
struct apply_range<0, meta_functor, indices...>
{
typedef typename meta_functor<indices...>::result result;
};
}
namespace compile_time
{
template<char... str>
struct string
{
static constexpr const char chars[sizeof...(str)+1] = {str..., '\0'};
};
template<char... str>
constexpr const char string<str...>::chars[sizeof...(str)+1];
template<typename lambda_str_type>
struct string_builder
{
template<unsigned... indices>
struct produce
{
typedef string<lambda_str_type{}.chars[indices]...> result;
};
};
}
#define CSTRING(string_literal) \
[]{ \
struct constexpr_string_type { const char * chars = string_literal; }; \
return variadic_toolbox::apply_range<sizeof(string_literal)-1, \
compile_time::string_builder<constexpr_string_type>::produce>::result{}; \
}()
#define FIXED_CSTRING(string_literal) \
([]{ \
struct constexpr_string_type { const char * chars = string_literal; }; \
return typename variadic_toolbox::apply_range<sizeof(string_literal)-1, \
compile_time::string_builder<constexpr_string_type>::template produce>::result{}; \
}())
struct A {
auto test() {
return FIXED_CSTRING("blah"); // works
// return CSTRING("blah"); // works too
}
template<typename X>
auto operator[](X) {
return 42;
}
};
template<typename T>
struct B {
auto test() {
// return CSTRING("blah");// does not compile
return FIXED_CSTRING("blah"); // works
}
};
int main() {
A a;
//return a[CSTRING("blah")]; // fails with error: two consecutive ' [ ' shall only introduce an attribute before ' [ ' token
return a[FIXED_CSTRING("blah")];
}
#QuarticCat의 답변에서 수정됨
template <char...>
struct Str
{
};
#define STRNAME(str) _constexpr_string_type_helper_##str
#define STR(str) \
auto STRNAME(str) = []<size_t... Is>(std::index_sequence<Is...>) \
{ \
constexpr char chars[] = #str; \
return Str<chars[Is]...>{}; \
} \
(std::make_index_sequence<sizeof(#str) - 1>{}); \
decltype(STRNAME(str))
람다 버전이 아닌 경우, 표준:::min 및 size of를 사용합니다.
끈의 길이는 256으로 제한되어 있습니다.
이 값은 dectype 또는 size of와 같은 평가되지 않은 컨텍스트에서 사용할 수 있습니다.
코드 크기를 줄이기 위해 스탬프 매크로를 사용했습니다.
#include <type_traits>
#include <utility>
template <char...>
struct Str
{
};
namespace char_mpl
{
constexpr auto first(char val, char...)
{
return val;
}
constexpr auto second(char, char val, char...)
{
return val;
}
template <class S1, class S2>
struct Concat;
template <char... lefts, char... rights>
struct Concat<Str<lefts...>, Str<rights...>>
{
using type = Str<lefts..., rights...>;
};
template <size_t right_count, class Right>
struct Take;
template <template <char...> class Right, char... vals>
struct Take<0, Right<vals...>>
{
using type = Str<>;
};
template <template <char...> class Right, char... vals>
struct Take<1, Right<vals...>>
{
using type = Str<first(vals...)>;
};
template <template <char...> class Right, char... vals>
struct Take<2, Right<vals...>>
{
using type = Str<first(vals...), second(vals...)>;
};
template <size_t lhs, size_t rhs>
concept greater = lhs > rhs;
// this may be improved for speed.
template <size_t n, char left, char... vals>
requires greater<n, 2> struct Take<n, Str<left, vals...>>
{
using type =
Concat<Str<left>, //
typename Take<n - 1, Str<vals...>>::type//
>::type;
};
};// namespace char_mpl
template <int length, char... vals>
struct RawStr
{
constexpr auto ch(char c, int i)
{
return c;
}
constexpr static auto to_str()
{
return
typename char_mpl::Take<length,
Str<vals...>>::type{};
}
};
#define STAMP4(n, STR, stamper) \
stamper(n, STR) stamper(n + 1, STR) \
stamper(n + 2, STR) stamper(n + 3, STR)
#define STAMP16(n, STR, stamper) \
STAMP4(n, STR, stamper) \
STAMP4(n + 4, STR, stamper) \
STAMP4(n + 8, STR, stamper) \
STAMP4(n + 12, STR, stamper)
#define STAMP64(n, STR, stamper) \
STAMP16(n, STR, stamper) \
STAMP16(n + 16, STR, stamper) \
STAMP16(n + 32, STR, stamper) \
STAMP16(n + 48, STR, stamper)
#define STAMP256(n, STR, stamper) \
STAMP64(n, STR, stamper) \
STAMP64(n + 64, STR, stamper) \
STAMP64(n + 128, STR, stamper) \
STAMP64(n + 192, STR, stamper)
#define STAMP(n, STR, stamper) stamper(STAMP##n, STR, n)
#define CH(STR, i) STR[std::min<size_t>(sizeof(STR) - 1, i)]
#define CSTR_STAMPER_CASE(n, STR) CH(STR, n),
#define CSTR_STAMPER(stamper, STR, n) \
RawStr<sizeof(STR) - 1, \
stamper(0, STR, CSTR_STAMPER_CASE) \
CH(STR, 256)>
#define CSTR(STR) (STAMP(256, STR, CSTR_STAMPER){}).to_str()
int main()
{
constexpr auto s = CSTR("12345");
decltype(CSTR("123123"));
sizeof(CSTR("123123"));
static_assert(
std::is_same_v<
Str<'1'>,
std::remove_cvref_t<decltype(CSTR("1"))>>);
static_assert(
std::is_same_v<
Str<'1', '2'>,
std::remove_cvref_t<decltype(CSTR("12"))>>);
static_assert(
std::is_same_v<
Str<'1', '2', '3', '4', '5'>,
std::remove_cvref_t<decltype(CSTR("12345"))>>);
}
@httax의 용액은 다음을 사용하여 더 짧을 수 있습니다.std::index_sequence
:
template<char...>
struct Str {};
template<class T, size_t... Is>
[[nodiscard]] constexpr auto helper(std::index_sequence<Is...>) {
return Str<T{}.chars[Is]...>{};
}
#define STR(str) \
[] { \
struct Temp { \
const char* chars = str; \
}; \
return helper<Temp>(std::make_index_sequence<sizeof(str) - 1>{}); \
}()
또는 더 짧습니다.
template<char...>
struct Str {};
#define STR(str) \
[]<size_t... Is>(std::index_sequence<Is...>) { \
return Str<str[Is]...>{}; \
} \
(std::make_index_sequence<sizeof(str) - 1>{})
당신이 찾고 있는 것은 문자열에 대한 N3599 리터럴 연산자 템플릿입니다.2013년에 C++용으로 제안되었으나 세부 사항에 대한 합의가 이루어지지 않았고 표준에 추가되지 않았습니다.
그러나 GCC와 Clang은 확장으로 지원합니다.문자열 리터럴을 다음 문자로 구성된 템플릿 매개 변수 팩으로 분할할 수 있습니다.
// some template type to represent a string
template <char... chars>
struct TemplateString {
static constexpr char value[] = { chars... };
template <char... chars2>
constexpr auto operator+(TemplateString<chars2...>) const {
// compile-time concatenation, oh yeah!
return TemplateString<chars..., chars2...>{};
}
};
// a custom user-defined literal called by the compiler when you use your _suffix
template <typename CharType, CharType... chars>
constexpr auto operator""_tstr () {
// since all the chars are constants here, you can do compile-time
// processing with constexpr functions and/or template metaprogramming,
// and then return whatever converted type you like
return TemplateString<chars...>{};
}
// auto = TemplateString<'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'>
constexpr auto str = "Hello"_tstr + " world!"_tstr;
cout << str.value << endl;
대안으로 매크로를 사용하는 트릭을 사용하면 동일한 위치로 이동할 수 있습니다(예를 들어, 답변에 웃는 팩스로 표시됨).
문자열 리터럴을 수락하고 constexpr char로 분할하는 방법은 확장자를 사용하거나 콜 사이트에서 매크로 해커리를 사용하는 두 가지뿐입니다.
은 자체구다같은방기합니다반로으의 접근 방식을 으로 합니다.Boost.Hana
string문자가 포함된 을 사용하지만 문자열(으)만 합니다.C++11
및 표준및constexpr
컴파일 시간에 대해 엄격한 검사를 수행하는 함수(컴파일 시간 표현식이 아닌 경우 컴파일 시간 오류일 수 있음).팬시 대신 일반적인 원시 C 문자열로 구성할 수 있습니다.{'a', 'b', 'c' }
(매크로를 통해).
구현: https://github.com/andry81/tacklelib/blob/master/include/tacklelib/tackle/tmpl_string.hpp
테스트: https://github.com/andry81/tacklelib/blob/master/src/tests/unit/test_tmpl_string.cpp
사용 예:
const auto s0 = TACKLE_TMPL_STRING(0, "012"); // "012"
const char c1_s0 = UTILITY_CONSTEXPR_GET(s0, 1); // '1'
const auto s1 = TACKLE_TMPL_STRING(0, "__012", 2); // "012"
const char c1_s1 = UTILITY_CONSTEXPR_GET(s1, 1); // '1'
const auto s2 = TACKLE_TMPL_STRING(0, "__012__", 2, 3); // "012"
const char c1_s2 = UTILITY_CONSTEXPR_GET(s2, 1); // '1'
// TACKLE_TMPL_STRING(0, "012") and TACKLE_TMPL_STRING(1, "012")
// - semantically having different addresses.
// So id can be used to generate new static array class field to store
// a string bytes at different address.
// Can be overloaded in functions with another type to express the compiletimeness between functions:
template <uint64_t id, typename CharT, CharT... tchars>
const overload_resolution_1 & test_overload_resolution(const tackle::tmpl_basic_string<id, CharT, tchars...> &);
template <typename CharT>
const overload_resolution_2 & test_overload_resolution(const tackle::constexpr_basic_string<CharT> &);
// , where `constexpr_basic_string` is another approach which loses
// the compiletimeness between function signature and body border,
// because even in a `constexpr` function the compile time argument
// looses the compiletimeness nature and becomes a runtime one.
한에세 ▁about▁constexpr
함수 컴파일 시간 경계: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexpr
기타 사용 방법에 대한 자세한 내용은 테스트를 참조하십시오.
현재 전체 프로젝트는 실험적입니다.
몇 가지 개선 사항이 있습니다.
#pragma once
// 姜安富 2023-6-17
// 编译期字符串拼接
// 使用方式:
// constexpr auto demo_1 = constr::Str("123");
// constexpr auto demo_2 = constr::Str({ '4', '5', '6', 0 }); // 这种方式定义字符串时,最后要多附加一个0
// constexpr auto demo_sum = demo_1 + demo_2;
namespace constr
{
// 创建整数序列
// 将整数转换为整数序列,如将 10 转换为 0,1,2,3,4,5,6,7,8,9
// 使用方式大致为:
// gen_seq<10>::type;
// 实现C++14的make_integer_sequence功能为了在C++11标准下使用
template<class T> using Invoke = typename T::type;
template<unsigned...> struct seq { using type = seq; };
template<class S1, class S2> struct concat;
template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1) + I2)...>
{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;
template<unsigned N>
struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> {};
template<> struct gen_seq<0> : seq<> {};
template<> struct gen_seq<1> : seq<0> {};
}
namespace constr
{
// 编译期字符串
template<int Size>
struct String
{
char str[Size + 1]; // 常字符串的数据
int size = Size; // 字符串数据长度,不包括最后的0字符
// 定义字符类型,让 Char<0>、Char<1>、Char<2>、Char<3>这样的类型都定义为char类型
template<char>
using Char = char;
// 构造函数1,为了使用 "abc"的形式来初始化str,需要使用整数序列
template <int... Index>
constexpr String(const char* str, seq<Index...>) :str{ str[Index]..., 0 }
{}
// 构造函数2,通过传入常字符串来构造对象,使用C++11提供的委托构造函数语法调用构造函数1
constexpr String(const char* str) : String(str, gen_seq<Size>())
{}
// 构造函数3,通过传入多个字符来拼接字符串
template <int... Index>
constexpr String(seq<Index...>, Char<Index>... str) : str{ str..., 0 }
{}
// 构造函数4,拷贝构造函数
template <int... Index>
constexpr String(const String<Size>& other) : str{ other.str[Index]..., 0 }
{}
// 构造函数5,用一个单字符初始化
constexpr String(char c) : str{ c , 0}
{}
// 构造函数6,初始化空编译期字符串
constexpr String() : str{ 0 }
{}
};
// 辅助字符串相加
struct Assist
{
template <int... AIndex,int... BIndex>
constexpr static String<sizeof...(AIndex) + sizeof...(BIndex)> Add(const String<sizeof...(AIndex)>& str1, const String<sizeof...(BIndex)>& str2, seq<AIndex...>, seq<BIndex...>)
{
// 调用constr::String的构造函数3来实现相加
return String<sizeof...(AIndex) + sizeof...(BIndex)>(gen_seq<sizeof...(AIndex) + sizeof...(BIndex)>(), str1.str[AIndex]..., str2.str[BIndex]...);
}
template <int... Index>
constexpr static String<sizeof...(Index) + 1> Add(const String<sizeof...(Index)>& str1, char c, seq<Index...>)
{
// 调用constr::String的构造函数3来实现加一个字符
return String<sizeof...(Index) + 1>(gen_seq<sizeof...(Index) + 1>(), str1.str[Index]..., c);
}
template <int... Index>
constexpr static String<sizeof...(Index) + 1> Add(char c, const String<sizeof...(Index)>& str1, seq<Index...>)
{
// 调用constr::String的构造函数3来实现加一个字符
return String<sizeof...(Index) + 1>(gen_seq<sizeof...(Index) + 1>(), c, str1.str[Index]...);
}
};
// 字符串加字符串
template<int ASize, int BSize>
constexpr String<ASize + BSize> operator+(const String<ASize>& str1, const String<BSize>& str2)
{
return Assist::Add(str1, str2, gen_seq<ASize>(), gen_seq<BSize>());
}
// 后加一个字符
template<int Size>
constexpr String<Size + 1> operator+(const String<Size>& str, char c)
{
return Assist::Add(str, c, gen_seq<Size>());
}
// 前加一个字符
template<int Size>
constexpr String<Size + 1> operator+(char c, const String<Size>& str)
{
return Assist::Add(c, str, gen_seq<Size>());
}
// 后加一个字符串 例: constr::str("123") + "abc";
template<int Size1, int I>
constexpr String<Size1 + I - 1> operator+(const String<Size1>& str1, const char(&str2)[I])
{
return str1 + String<I - 1>(str2);
}
// 前加一个字符串 例: "abc" + constr::str("123");
template<int Size1, int I>
constexpr String<Size1 + I - 1> operator+(const char(&str1)[I], const String<Size1>& str2)
{
return String<I - 1>(str1) + str2;
}
template<int I>
constexpr String<I - 1> Str(const char(&a)[I])
{
return String<I - 1>(a);
}
constexpr String<0> Str()
{
return String<0>();
}
constexpr String<1> Str(char c)
{
return String<1>(c);
}
}
테스트:
int main(int argc, char** argv)
{
constexpr auto demo_1 = constr::Str("123");
constexpr auto demo_2 = constr::Str({ '4', '5', '6', '\0'});
constexpr auto demo_3 = demo_1 + demo_2;
constexpr auto demo_4 = demo_1 + 'a';
constexpr auto demo_5 = 'a' + demo_1;
constexpr auto demo_6 = demo_1 + "abc";
constexpr auto demo_7 = "abc" + demo_1 ;
constexpr auto demo_8 = constr::Str('a');
constexpr auto demo_9 = constr::Str();
std::cout << "demo_3.size = " << demo_3.size << std::endl;
std::cout << "demo_3.str = " << demo_3.str << std::endl;
std::cout << "demo_4.str = " << demo_4.str << std::endl;
std::cout << "demo_5.str = " << demo_5.str << std::endl;
std::cout << "demo_6.str = " << demo_6.str << std::endl;
std::cout << "demo_7.str = " << demo_7.str << std::endl;
std::cout << "demo_8.str = " << demo_8.str << std::endl;
std::cout << "demo_9.str = " << demo_9.str << std::endl;
return 0;
}
테스트 결과:
demo_3.size = 6
demo_3.str = 123456
demo_4.str = 123a
demo_5.str = a123
demo_6.str = 123abc
demo_7.str = abc123
demo_8.str = a
demo_9.str =
언급URL : https://stackoverflow.com/questions/15858141/conveniently-declaring-compile-time-strings-in-c
'programing' 카테고리의 다른 글
Swift를 사용하여 한 View 컨트롤러에서 다른 View 컨트롤러로 이동하는 방법 (0) | 2023.08.17 |
---|---|
동일한 쿼리에서 DISTINCT 및 TOP 사용 (0) | 2023.08.17 |
JQuery.trigger에서 매개 변수 전달 중 (0) | 2023.08.17 |
jQuery를 사용하여 경로 없이 파일 입력의 선택한 파일 이름 가져오기 (0) | 2023.08.17 |
다른 속성 파일에 속성 파일을 포함할 수 있는 방법이 있습니까? (0) | 2023.08.17 |