C ++ concat dos literales de cadenas `const char`

¿Es posible combinar dos cadenas literales usando un constexpr ? O reformulado puede uno eliminar macros en código como:

 #define nl(str) str "\n" int main() { std::cout << nl("usage: foo") nl("print a message") ; return 0; } 

Actualización : no hay nada de malo con el uso de "\n" , sin embargo, me gustaría saber si se puede usar constexpr para reemplazar ese tipo de macros.

  1. Sí, es completamente posible crear cadenas constantes en tiempo de comstackción y manipularlas con funciones constexpr e incluso operadores. Sin embargo,

  2. El comstackdor no está obligado a realizar la inicialización constante de ningún objeto que no sea estático y los objetos de duración de subproceso. En particular, los objetos temporales (que no son variables, y tienen algo menos que la duración de almacenamiento automático) no requieren inicialización constante, y hasta donde yo sé, ningún comstackdor hace eso para las matrices. Consulte 3.6.2 / 2-3, que define la inicialización constante, y 6.7.4 para obtener más información sobre las variables de duración estática de nivel de bloque. Ninguno de estos se aplica a los temporales, cuya duración se define en 12.2 / 3 y siguientes.

Para que pueda lograr la concatenación de tiempo de comstackción deseada con:

 static const auto conc = ; std::cout < < conc; 

pero no puedes hacer que funcione con:

 std::cout < < ; 

Actualizar:

Pero puedes hacer que funcione con:

 std::cout < < *[]()-> const { static constexpr auto s = /* constexpr call */; return &s;}() < < " some more text"; 

Pero la puntuacion repetitiva es demasiado fea como para hacerla mas que un pequeño truco interesante.


(Descargo de responsabilidad: IANALL, aunque a veces me gusta jugar uno en internet. Así que puede haber algunos polvorientos rincones del estándar que contradigan lo anterior).

(A pesar de la cláusula de exención de responsabilidad, e impulsado por @DyP, agregué algunas citas de abogados más).

Un poco de constexpr , salpicado con algunos TMP y un encabezado de índices me da esto:

 #include  template struct seq{}; template struct gen_seq : gen_seq{}; template struct gen_seq<0, Is...> : seq{}; template constexpr std::array concat(char const (&a1)[N1], char const (&a2)[N2], seq, seq){ return {{ a1[I1]..., a2[I2]... }}; } template constexpr std::array concat(char const (&a1)[N1], char const (&a2)[N2]){ return concat(a1, a2, gen_seq{}, gen_seq{}); } 

Ejemplo en vivo

Lo aclararía un poco más, pero tengo que irme y quería dejarlo antes de eso. Deberías poder trabajar desde eso.

A primera vista, los literales de cadena definidos por el usuario C ++ 11 parecen ser un enfoque mucho más simple. (Si, por ejemplo, está buscando una forma de habilitar y deshabilitar globalmente la inyección de nueva línea en tiempo de comstackción)

  • No puede devolver una matriz (simple) de una función.
  • No puede crear un nuevo const char[n] dentro de un constexpr (§7.1.5 / 3 dcl.constexpr).
  • Una expresión de constante de dirección debe referirse a un objeto de duración de almacenamiento estático (§5.19 / 3 expr.const) – esto no permite algunos trucos con objetos de tipos que tienen un constestador ensamblando el arreglo para la concatenación y su constexto simplemente convirtiéndolo en un ptr .
  • Los argumentos pasados ​​a un constexpr no se consideran constantes de tiempo de comstackción, por lo que puede usar el fct en tiempo de ejecución también, esto no permite algunos trucos con la metaprogtwigción de plantillas.
  • No se puede obtener el único carácter de cadena de un literal pasado a una función como argumentos de plantilla; esto no permite otros trucos de metaprogtwigción de plantillas.

Entonces (hasta donde yo sé), no puede obtener un constexpr que devuelve una char const* de una cadena recién construida o una char const[n] . Tenga en cuenta que la mayoría de estas restricciones no son std::array para una std::array como lo señala Xeo.

E incluso si pudieras devolver algo de char const* , un valor de retorno no es un literal, y solo los literales de cadena adyacentes se concatenan. Esto sucede en la fase de traducción 6 (§2.2), que todavía llamaría una fase de preprocesamiento. Constexpr se evalúan más tarde (ref?). ( f(x) f(y) donde f es una función es un error de syntax afaik)

Pero puede devolver desde su constexto un objeto de otro tipo (con un constestador o que sea un agregado) que contenga ambas cadenas y pueda insertarse / imprimirse en un valor basic_ostream .


Editar: aquí está el ejemplo. Es bastante largo. Note que puede simplificar esto para obtener una “\ n” adicional para agregar el final de una cadena. (Este es más un enfoque genérico que acabo de escribir de memoria).

Edit2: en realidad, no puedes optimizarlo realmente. Crear el miembro de datos arr como una “matriz de const tipo_carpeta” con la ‘\ n’ incluida (en lugar de una matriz de literales de cadena) utiliza un código de plantilla variopinto que en realidad es un poco más largo (pero funciona, ver la respuesta de Xeo).

Nota: como ct_string_vector (el nombre no es bueno) almacena punteros, debe usarse solo con cadenas de duración de almacenamiento estático (como literales o variables globales). La ventaja es que una cadena no tiene que ser copiada y expandida por mecanismos de plantilla. Si usa un constexpr para almacenar el resultado (como en el ejemplo main ), el comstackdor debería quejarse si los parámetros pasados ​​no tienen una duración de almacenamiento estática.

 #include  #include  #include  template < typename T_Char, std::size_t t_len > struct ct_string_vector { using char_type = T_Char; using stringl_type = char_type const*; private: stringl_type arr[t_len]; public: template < typename... TP > constexpr ct_string_vector(TP... pp) : arr{pp...} {} constexpr std::size_t length() { return t_len; } template < typename T_Traits > friend std::basic_ostream < char_type, T_Traits >& operator < <(std::basic_ostream < char_type, T_Traits >& o, ct_string_vector const& p) { std::copy( std::begin(p.arr), std::end(p.arr), std::ostream_iterator(o) ); return o; } }; template < typename T_String > using get_char_type = typename std::remove_const < typename std::remove_pointer < typename std::remove_reference < typename std::remove_extent < T_String > :: type > :: type > :: type > :: type; template < typename T_String, typename... TP > constexpr ct_string_vector < get_char_type, 1+sizeof...(TP) > make_ct_string_vector( T_String p, TP... pp ) { // can add an "\n" at the end of the {...} // but then have to change to 2+sizeof above return {p, pp...}; } // better version of adding an '\n': template < typename T_String, typename... TP > constexpr auto add_newline( T_String p, TP... pp ) -> decltype( make_ct_string_vector(p, pp..., "\n") ) { return make_ct_string_vector(p, pp..., "\n"); } int main() { // ??? (still confused about requirements of constant init, sry) static constexpr auto assembled = make_ct_string_vector("hello ", "world"); enum{ dummy = assembled.length() }; // enforce compile-time evaluation std::cout < < assembled << std::endl; std::cout << add_newline("first line") << "second line" << std::endl; } 

No, para constexpr necesita una función legal en primer lugar, y las funciones no pueden pegar, etc. de argumentos literales de cadena.

Si piensas en la expresión equivalente en una función regular, sería asignar memoria y concatenar las cadenas, definitivamente no susceptible de constexpr .