C-Style Strings como argumentos de plantilla?

¿Se pueden usar cadenas C-Style como argumentos de plantilla?

Lo intenté:

template  struct X { const char *GetString() const { return str; } }; int main() { X x; cout<<x.GetString(); } 

Y aunque no tengo quejas sobre la definición de clase, la instanciación produce 'X' : invalid expression as a template argument for 'str' (VC).

Un literal de cadena no se puede usar como un argumento de plantilla .

Actualización: hoy en día, hace unos pocos años se formuló y respondió esta pregunta, es posible utilizar literales de cadena como argumentos de plantilla. Con C ++ 11, podemos usar los paquetes de caracteres como argumentos de plantilla ( template ) y es posible pasar una cadena literal a dicha plantilla.

Esto funcionaría, sin embargo:

 template  struct X { const char *GetString() const { return str; } }; char global_string[] = "String"; int main() { X x; cout< 

Lo sé, este tema es un poco viejo, pero pongo este comentario si alguien está interesado. Logré plantillas con pasar cadena literal como argumento con combinación de MACROS.

Hice un ejemplo de código,


#include  #include  #include  #include  #include  using namespace std; #define MAX_CONST_CHAR 100 #define MIN(a,b) (a)<(b)?(a):(b) #define _T(s)\ getChr(s,0),\ getChr(s,1),\ getChr(s,2),\ getChr(s,3),\ getChr(s,4),\ getChr(s,5),\ getChr(s,6),\ getChr(s,7),\ getChr(s,8),\ getChr(s,9),\ getChr(s,10),\ getChr(s,11),\ getChr(s,12),\ getChr(s,13),\ getChr(s,14),\ getChr(s,15),\ getChr(s,16),\ getChr(s,17),\ getChr(s,18),\ getChr(s,19),\ getChr(s,20),\ getChr(s,21),\ getChr(s,22),\ getChr(s,23),\ getChr(s,24),\ getChr(s,25),\ getChr(s,26),\ getChr(s,27),\ getChr(s,28),\ getChr(s,29),\ getChr(s,30),\ getChr(s,31),\ getChr(s,32),\ getChr(s,33),\ getChr(s,34),\ getChr(s,35),\ getChr(s,36),\ getChr(s,37),\ getChr(s,38),\ getChr(s,39),\ getChr(s,40),\ getChr(s,41),\ getChr(s,42),\ getChr(s,43),\ getChr(s,44),\ getChr(s,45),\ getChr(s,46),\ getChr(s,47),\ getChr(s,48),\ getChr(s,49),\ getChr(s,50),\ getChr(s,51),\ getChr(s,52),\ getChr(s,53),\ getChr(s,54),\ getChr(s,55),\ getChr(s,56),\ getChr(s,57),\ getChr(s,58),\ getChr(s,59),\ getChr(s,60),\ getChr(s,61),\ getChr(s,62),\ getChr(s,63),\ getChr(s,64),\ getChr(s,65),\ getChr(s,66),\ getChr(s,67),\ getChr(s,68),\ getChr(s,69),\ getChr(s,70),\ getChr(s,71),\ getChr(s,72),\ getChr(s,72),\ getChr(s,72),\ getChr(s,73),\ getChr(s,74),\ getChr(s,75),\ getChr(s,76),\ getChr(s,77),\ getChr(s,78),\ getChr(s,79),\ getChr(s,80),\ getChr(s,81),\ getChr(s,82),\ getChr(s,83),\ getChr(s,84),\ getChr(s,85),\ getChr(s,86),\ getChr(s,87),\ getChr(s,88),\ getChr(s,89),\ getChr(s,90),\ getChr(s,91),\ getChr(s,92),\ getChr(s,93),\ getChr(s,94),\ getChr(s,95),\ getChr(s,96),\ getChr(s,97),\ getChr(s,98),\ getChr(s,99),\ getChr(s,100) #define getChr(name, ii) ((MIN(ii,MAX_CONST_CHAR)) class E { public: string *str; E(){ std::vector vec = {Chars_...}; str = new string(vec.begin(),vec.end()); } ~E() { delete str; } }; int main(int argc, char *argv[]) { E<_T("Any template can pass const strings literals")> e; printf("%s",e.str->c_str()); } 

Esto funciona con g ++ 4.6 y pasando el argumento -std = c ++ 0x, y tiene un límite de 100 caracteres pero, por supuesto, puede ser tan grande como lo desee. Tal vez esta técnica no está bien optimizada, pero será más productiva que declarar las variables externas necesarias (estoy seguro;))

Restricciones: la cadena literal debe ser el último y último argumento de la plantilla debido al paso de argumentos de variadics.

EDITAR : Gracias a Padek, probó que esta pieza de código también funciona con Visual Studio 2017, pero cambia el tamaño por tamaño de (nombre) / tamaño de (* nombre) .

Perdón por publicar en una pregunta tan antigua, pero esto es lo que creo que es el enfoque más limpio para pasar un literal como argumento sin usar almacenamiento.

Codifica la cadena como un tipo:

 template  using tstring = std::integer_sequence; 

Crear un operador literal definido por el usuario:

 template  constexpr tstring operator""_tstr() { return { }; } 

Y use especialización parcial para recuperar los datos del personaje según sea necesario:

 template  struct X; template  struct X> { const char* GetString() const { static constexpr char str[sizeof...(elements) + 1] = { elements..., '\0' }; return str; } }; 

Esto te permite escribir:

 X 

El literal definido por el usuario utiliza una funcionalidad no estándar ( n3599 ) que no está en C ++ 14 pero que es compatible con las comstackciones recientes de GCC y Clang, y con suerte se reconsiderará para C ++ 1z.

No, no puede trabajar con literales de cadenas en tiempo de comstackción. Lo mejor que puede obtener son los literales de varios caracteres extraños (por ejemplo, 'abcd' ) que utilizan algunos analizadores en tiempo de comstackción. Se mencionan en §2.13.2.1 :

Un literal de carácter ordinario que contiene más de un c-char es un literal de múltiples caracteres. Un literal multicharacter tiene un tipo int y un valor definido por la implementación.

Sin embargo, en C ++ 0x puede haber formas de evitar estas limitaciones con los nuevos literales de cadenas , Arctic Interactive tiene un interesante artículo sobre eso.

Con C ++ 11 puede representar razonablemente cadenas literales como argumentos de plantilla variadic, es decir, una colección de parámetros de plantilla int. He creado un ejemplo de prueba de concepto que configura una de esas plantillas sin tener que escribir manualmente foo<16, 73, 51 ...> para cada cadena.

Ejemplo:

 // The template we want to pass a string to template  struct foo { // It needs one helper function for decltype magic, this could be avoided though template  static foo add_one(); }; // This is the string we want to use with foo, simulating foo<"Hello world!" __FILE__>: constexpr const char *teststr = "Hello world!" __FILE__; // Get char N of a string literal constexpr int strchr(const char *str, int N) { return str[N]; } // recursive helper to build the typedef from teststr template  struct builder { typedef typename builder::type child; typedef decltype(child::template add_one()) type; }; template  struct builder { typedef foo type; }; // compile time strlen constexpr int slen(const char *str) { return *str ? 1 + slen(str+1) : 0; } int main() { builder::type test; // compile error to force the type to be printed: int foo = test; } 

Necesitarás al menos gcc 4.6 para constexpr y podría usar un poco de esmalte, pero el error del comstackdor que recibo indica que el tipo se está construyendo correctamente:

 error: cannot convert 'builder<19>::type {aka foo<72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 115, 108, 105, 116, 46, 99, 99, 0>}' to 'int' in initializatio 

No. De acuerdo con el estándar C ++ 14.3.2 / 1:

Un argumento de plantilla para un parámetro de plantilla que no sea de tipo y sin plantilla será uno de:
– una expresión constante constante de tipo integral o de enumeración; o
– el nombre de un parámetro de plantilla sin tipo; o
– la dirección de un objeto o función con vinculación externa, incluidas plantillas de función e ids de plantilla de función pero excluyendo miembros de clase no estáticos, expresada como & id-expression donde & es opcional si el nombre hace referencia a una función o matriz, o si el parámetro de plantilla correspondiente es una referencia, o
– un puntero al miembro expresado como se describe en 5.3.1.

Las cadenas no están en la lista.

Puede usar la dirección de la cadena con enlace externo como un parámetro de plantilla, por ejemplo:

 template  class My { public: void do_stuff() { std::cout << "zzz"; } }; const char* str; int main() { My<&str> zz; zz.do_stuff(); printf("Result: %d %d \n", 60 % 10 + 1, (60 % 10 ) + 1 ); } 

C ++ no sabe sobre cadenas. Solo sabe sobre “matrices de caracteres” y allí el literal sería el puntero a la matriz. Entonces, si usara el “valor” de su cadena como parámetro de plantilla, realmente usaría el valor del puntero.