Convenientemente declarando cadenas de tiempo de comstackción en C ++

Ser capaz de crear y manipular cadenas durante el tiempo de comstackción en C ++ tiene varias aplicaciones útiles. Aunque es posible crear cadenas de tiempo de comstackción en C ++, el proceso es muy engorroso, ya que la cadena debe declararse como una secuencia variadica de caracteres, por ejemplo

using str = sequence; 

Las operaciones como la concatenación de cadenas, la extracción de subcadenas y muchas otras se pueden implementar fácilmente como operaciones en secuencias de caracteres. ¿Es posible declarar cadenas de tiempo de comstackción más convenientemente? De lo contrario, ¿hay alguna propuesta en curso que permita una statement conveniente de cadenas de tiempo de comstackción?

Por qué los enfoques existentes fallan

Idealmente, nos gustaría poder declarar cadenas en tiempo de comstackción de la siguiente manera:

 // Approach 1 using str1 = sequence; 

o, usando literales definidos por el usuario,

 // Approach 2 constexpr auto str2 = "Hello, world!"_s; 

donde decltype(str2) tendría un constructor constexpr . Es posible implementar una versión más desordenada del enfoque 1, aprovechando el hecho de que puede hacer lo siguiente:

 template  struct foo; 

Sin embargo, la matriz necesitaría tener un enlace externo, por lo que para que el enfoque 1 funcione, tendríamos que escribir algo como esto:

 /* Implementation of array to sequence goes here. */ constexpr const char str[] = "Hello, world!"; int main() { using s = string; return 0; } 

No hace falta decir que esto es muy inconveniente. El enfoque 2 en realidad no es posible de implementar. Si tuviéramos que declarar un operador literal ( constexpr ), ¿cómo podríamos especificar el tipo de devolución? Ya que necesitamos que el operador devuelva una secuencia variada de caracteres, entonces necesitaríamos usar el parámetro const char* para especificar el tipo de retorno:

 constexpr auto operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */ 

Esto da como resultado un error de comstackción, porque s no es un constexpr . Tratar de evitar esto haciendo lo siguiente no ayuda mucho.

 template  constexpr sequence operator"" _s() { return {}; } 

El estándar dicta que este formulario de operador literal específico está reservado para tipos enteros y de coma flotante. Mientras que 123_s funcionarían, abc_s no lo haría. ¿Qué pasa si abandonamos por completo los literales definidos por el usuario, y simplemente usamos una función constexpr regular?

 template  constexpr auto string(const char (&array)[Size]) -> /* Some metafunction using `array` */ 

Como antes, nos encontramos con el problema de que la matriz, ahora un parámetro para la función constexpr , ya no es un tipo constexpr .

Creo que debería ser posible definir una macro de preprocesador C que toma una cadena y el tamaño de la cadena como argumentos, y devuelve una secuencia que consta de los caracteres en la cadena (usando BOOST_PP_FOR , stringification, subíndices de matriz y similares). Sin embargo, no tengo el tiempo (o el interés suficiente) para implementar dicho macro =)

No he visto nada que coincida con la elegancia del str_const de Scott Schurr presentado en C ++ Now 2012 . Sin embargo, requiere constexpr .

A continuación, le mostramos cómo puede usarlo y qué puede hacer:

 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! } 

¡No se pone mucho más frío que la verificación del rango en tiempo de comstackción!

Tanto el uso como la implementación están libres de macros. Y no hay un límite artificial en el tamaño de la cuerda. Publicaría la implementación aquí, pero estoy respetando los derechos de autor implícitos de Scott. La implementación está en una sola diapositiva de su presentación vinculada a la anterior.

Creo que debería ser posible definir una macro de preprocesador C que toma una cadena y el tamaño de la cadena como argumentos, y devuelve una secuencia que consta de los caracteres en la cadena (usando BOOST_PP_FOR, stringification, subíndices de matriz y similares). Sin embargo, no tengo el tiempo (o el interés suficiente) para implementar tal macro

Es posible implementar esto sin depender de boost, usando macro muy simple y algunas de las características de C ++ 11:

  1. lambdas variadic
  2. plantillas
  3. expresiones constantes generalizadas
  4. Inicializadores de miembros de datos no estáticos
  5. Inicialización uniforme

(los dos últimos no son estrictamente necesarios aquí)

  1. necesitamos poder instanciar una plantilla variadica con indicios suministrados por el usuario de 0 a N – una herramienta también útil por ejemplo para expandir tupla en el argumento de la función de plantilla variadic (ver preguntas: ¿Cómo puedo expandir una tupla en los argumentos de la función de la plantilla variadic?
    “desempaquetar” una tupla para llamar a un puntero de función coincidente )

     namespace variadic_toolbox { template class meta_functor, unsigned... indices> struct apply_range { typedef typename apply_range::result result; }; template class meta_functor, unsigned... indices> struct apply_range<0, meta_functor, indices...> { typedef typename meta_functor::result result; }; } 
  2. a continuación, defina una plantilla variadica llamada cadena con parámetro no de tipo char:

     namespace compile_time { template struct string { static constexpr const char chars[sizeof...(str)+1] = {str..., '\0'}; }; template constexpr const char string::chars[sizeof...(str)+1]; } 
  3. ahora la parte más interesante: pasar caracteres literales a la plantilla de cadena:

     namespace compile_time { template struct string_builder { template struct produce { typedef string result; }; }; } #define CSTRING(string_literal) \ []{ \ struct constexpr_string_type { const char * chars = string_literal; }; \ return variadic_toolbox::apply_range::produce>::result{}; \ }() 

una demostración de concatenación simple muestra el uso:

  namespace compile_time { template string operator*(string, string) { 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; } 

https://ideone.com/8Ft2xu

Editar: como señaló Howard Hinnant (y algo de mí en mi comentario al OP), es posible que no necesite un tipo con cada carácter de la cadena como argumento de una sola plantilla. Si necesita esto, hay una solución sin macro a continuación.

Hay un truco que encontré al intentar trabajar con cadenas de caracteres en el momento de la comstackción. Requiere introducir otro tipo además de la “cadena de plantilla”, pero dentro de las funciones, puede limitar el scope de este tipo.

No utiliza macros, sino algunas características de C ++ 11.

 #include  // helper function constexpr unsigned c_strlen( char const* str, unsigned count = 0 ) { return ('\0' == str[0]) ? count : c_strlen(str+1, count+1); } // helper "function" struct template < char t_c, char... tt_c > struct rec_print { static void print() { std::cout << t_c; rec_print < tt_c... > :: print (); } }; template < char t_c > struct rec_print < t_c > { static void print() { std::cout << t_c; } }; // destination "template string" type template < char... tt_c > struct exploded_string { static void print() { rec_print < tt_c... > :: print(); } }; // struct to explode a `char const*` to an `exploded_string` type template < typename T_StrProvider, unsigned t_len, char... tt_c > struct explode_impl { using result = typename explode_impl < T_StrProvider, t_len-1, T_StrProvider::str()[t_len-1], tt_c... > :: result; }; template < typename T_StrProvider, char... tt_c > struct explode_impl < T_StrProvider, 0, tt_c... > { using result = exploded_string < tt_c... >; }; // syntactical sugar template < typename T_StrProvider > using explode = typename explode_impl < T_StrProvider, c_strlen(T_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(); } 

Si no quieres usar la solución de Boost puedes crear macro simple que hará algo similar:

 #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; 

el único problema es el tamaño fijo de 64 caracteres (más cero adicional). Pero puede cambiarse fácilmente según sus necesidades.

Creo que debería ser posible definir una macro de preprocesador C que toma una cadena y el tamaño de la cadena como argumentos, y devuelve una secuencia que consta de los caracteres en la cadena (usando BOOST_PP_FOR, stringification, subíndices de matriz, etc.)

Hay un artículo: Uso de cadenas en metaprogtwigs de plantilla C ++ por Abel Sinkovics y Dave Abrahams.

Tiene alguna mejora sobre su idea de usar macro + BOOST_PP_REPEAT – no requiere pasar el tamaño explícito a macro. En resumen, se basa en un límite superior fijo para el tamaño de cadena y “protección de desbordamiento de cadena”:

 template  constexpr char at(char const(&s)[N], int i) { return i >= N ? '\0' : s[i]; } 

más impulso condicional :: mpl :: push_back .


Cambié mi respuesta aceptada a la solución de Yankes, ya que resuelve este problema específico, y lo hace elegantemente sin el uso de constexpr o código de preprocesador complejo.

Si acepta ceros al final, bucle de macros escrito a mano, 2 veces la repetición de la cadena en macro expandido, y no tiene Boost, entonces estoy de acuerdo, es mejor. Sin embargo, con Boost serían solo tres líneas:

DEMO EN VIVO

 #include  #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 

Un colega me desafió a concatenar cadenas en memoria en tiempo de comstackción. Incluye la instanciación de cadenas individuales en tiempo de comstackción también. El listado completo del código está aquí:

 //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  using std::size_t; //wrapper for const char* to "allocate" space for it at compile-time template 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 constexpr String(Args... args):_str{ args... } { } const char _str[N]; }; //takes variadic number of chars, creates String object from it. //ie myMakeStringFromChars('f', 'o', 'o', '\0') -> String<4>::_str = "foo" template constexpr auto myMakeStringFromChars(Args... args) -> String { return String(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. //This class exists only to dispatch on the recursion being finished or not. //The default below continues recursion. template struct RecurseOrStop { template static constexpr String 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 { template static constexpr String 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 constexpr String myRecurseOrStop(const char* str, Args... args) { //template needed after :: since the compiler needs to distinguish //between recurseOrStop being a function template with 2 patwigters //or an enum being compared to N (recurseOrStop < N) return RecurseOrStop::template recurseOrStop(str, args...); } //implementation of the declaration above //add a character to the end of the parameter pack and recurse to next character. template template constexpr String RecurseOrStop::recurseOrStop(const char* str, Args... args) { return myRecurseOrStop(str, args..., str[I]); } //implementation of the declaration above //terminate recursion and construct string from full list of characters. template constexpr String RecurseOrStop::recurseOrStop(const char* str, Args... args) { return myMakeStringFromChars(args...); } //takes a compile-time static string literal and returns String from it //this happens by transforming the string literal into a variadic patwigter //pack of char. //ie myMakeString("foo") -> calls myMakeStringFromChars('f', 'o', 'o', '\0'); template constexpr String myMakeString(const char (&str)[N]) { return myRecurseOrStop(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 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 struct MyTuple: public MyTupleLeaf... { constexpr MyTuple(Args... args):MyTupleLeaf(args)... { } }; //Helper function akin to std::make_tuple. Needed since functions can deduce //types from parameter values, but classes can't. template constexpr MyTuple myMakeTuple(Args... args) { return MyTuple(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. //ie ("foo", "foobar") -> (const char (&arg1)[4], const char (&arg2)[7]) params -> // -> MyTuple, String<7>> return value template constexpr auto myMakeStrings(const char (&...args)[Sizes]) -> MyTuple...> { //expands into myMakeTuple(myMakeString(arg1), myMakeString(arg2), ...) return myMakeTuple(myMakeString(args)...); } //Prints tuple of strings template //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(&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"; } 

basado en la idea de Howard Hinnant puedes crear una clase literal que agregará dos literales juntos.

 template using charDummy = char; template struct F { const char table[sizeof...(dummy) + 1]; constexpr F(const char* a) : table{ str_at(a)..., 0} { } constexpr F(charDummy... a) : table{ a..., 0} { } constexpr F(const F& a) : table{ a.table[dummy]..., 0} { } template constexpr F operator+(F b) { return { this->table[dummy]..., b.table[dummyB]... }; } }; template struct get_string { constexpr static auto g(const char* a) -> decltype( get_string::g(a) + F<0>(a + I)) { return get_string::g(a) + F<0>(a + I); } }; template<> struct get_string<0> { constexpr static F<0> g(const char* a) { return {a}; } }; template constexpr auto make_string(const char (&a)[I]) -> decltype( get_string::g(a) ) { return get_string::g(a); } constexpr auto a = make_string("abc"); constexpr auto b = a+ make_string("def"); // b.table == "abcdef" 

Aquí hay una solución sucinta de C ++ 14 para crear un std :: tuple para cada cadena de tiempo de comstackción pasada.

 #include  #include  namespace detail { template  decltype(auto) build_string(const char * str, std::index_sequence) { return std::make_tuple(str[indices]...); } } template  constexpr decltype(auto) make_string(const char(&str)[N]) { return detail::build_string(str, std::make_index_sequence()); } auto HelloStrObject = make_string("hello"); 

Y aquí hay uno para crear un tipo de tiempo de comstackción único, recortado desde la otra macro publicación.

 #include  template  struct String {}; template  decltype(auto) build_string(std::index_sequence) { return String(); } #define make_string(str) []{\ struct Str { const char * chars = str; };\ return build_string(std::make_index_sequence());\ }() auto HelloStrObject = make_string("hello"); 

Es una lástima que los literales definidos por el usuario no puedan usarse para esto todavía.

Mientras jugaba con el mapa de hana boost, encontré este hilo. Como ninguna de las respuestas resolvió mi problema, encontré una solución diferente que quiero agregar aquí ya que podría ser potencialmente útil para otros.

Mi problema fue que al usar el mapa de hana boost con hana strings, el comstackdor aún generaba algún código de tiempo de ejecución (ver a continuación). La razón fue obviamente que para consultar el mapa en tiempo de comstackción debe ser constexpr . Esto no es posible ya que la macro BOOST_HANA_STRING genera un lambda, que no se puede usar en contexto constexpr . Por otro lado, el mapa necesita cadenas con diferentes contenidos para ser diferentes tipos.

Como las soluciones en este hilo están usando un lambda o no proporcionan diferentes tipos para diferentes contenidos, encontré útil el siguiente enfoque. También evita la syntax hacky str<'a', 'b', 'c'> .

La idea básica es tener una versión de str_const de Scott Schurr str_const plantilla en el hash de los personajes. Es c++14 , pero c++11 debería ser posible con una implementación recursiva de la función crc32 (ver aquí ).

 // str_const from https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf?raw=true #include  template ////// <- This is the difference... class str_const2 { // constexpr string private: const char* const p_; const std::size_t sz_; public: template 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 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 for use in map #define CSTRING_TYPE(text) hana::type_c( text ))> 

Uso:

 #include  #include  #include  #include  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; } 

Resulting assembler code with clang-cl 5.0 is:

 012A1370 mov eax,2 012A1375 ret 

Your approach #1 is the correct one.

However, the array would need to have external linkage, so to get approach 1 to work, we would have to write something like this: constexpr const char str[] = “Hello, world!”;

No, not correct. This compiles with clang and gcc. I hope its standard c++11, but i am not a language laywer.

 #include  template  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  void print() { //String as template parameter std::cout << Name::c_str(); } int main() { std::cout << Hello_World_t::c_str() << std::endl; print(); return 0; } 

What I would really love for c++17 would be the following to be equivalent (to complete approach #1)

 // for template  <"Text"> == <'T','e','x','t'> 

Something very similar already exists in the standard for templated user defined literals,as void-pointer also mentions, but only for digits. Until then another little trick is to use the override editing mode + copy and paste of

 string_t<' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '>; 

If you do not mind the macro, than this works(slighty modified from Yankes answer):

 #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//guard for longer strings print(); 

Nobody seems to like my other answer :-<. So here I show how to convert a str_const to a real type:

 #include  #include  // constexpr string with const member functions class str_const { private: const char* const p_; const std::size_t sz_; public: template 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  struct string_t{ static char const * c_str() { static constexpr char string[]={letters...,'\0'}; return string; } }; template auto constexpr expand(std::index_sequence){ return string_t{}; } template using string_const_to_type = decltype(expand(std::make_index_sequence{})); constexpr str_const hello{"Hello World"}; using hello_t = string_const_to_type; int main() { // char c = hello_t{}; // Compile error to print type std::cout << hello_t::c_str(); return 0; } 

Compiles with clang++ -stdlib=libc++ -std=c++14 (clang 3.7)

kacey’s solution for creating a unique compile-time type can, with minor modifications, also be used with C++11:

 template  struct string_t {}; namespace detail { template  struct make_string_t : make_string_t {}; template  struct make_string_t { typedef string_t type; }; } // namespace detail #define CSTR(str) []{ \ struct Str { const char *chars = str; }; \ return detail::make_string_t::type(); \ }() 

Utilizar:

 template  void test(String) { // ... String = string_t<'H','e','l','l','o','\0'> } test(CSTR("Hello"));