c++ - not - handlebars partials
C-Style Strings como argumentos de plantilla? (8)
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.
Esta pregunta ya tiene una respuesta aquí:
¿Se pueden usar cadenas C-Style como argumentos de plantilla?
Lo intenté:
template <char *str>
struct X
{
const char *GetString() const
{
return str;
}
};
int main()
{
X<"String"> 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).
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 <int... Args>
struct foo {
// It needs one helper function for decltype magic, this could be avoided though
template <int N>
static foo<N, Args...> 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 <int N, int P=0>
struct builder {
typedef typename builder<N, P+1>::type child;
typedef decltype(child::template add_one<strchr(teststr,P)>()) type;
};
template <int N>
struct builder<N,N> {
typedef foo<strchr(teststr, N)> type;
};
// compile time strlen
constexpr int slen(const char *str) {
return *str ? 1 + slen(str+1) : 0;
}
int main() {
builder<slen(teststr)>::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 compilador 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
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 <stdio.h>
#include <iostream>
#include <vector>
#include <memory>
#include <string.h>
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))<sizeof(name)/sizeof(*name)?name[ii]:0)
template <char... Chars_>
class E {
public:
string *str;
E(){
std::vector<char> 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) .
No, no puede trabajar con literales de cadenas en tiempo de compilación. Lo mejor que puede obtener son los literales de varios caracteres extraños (por ejemplo, ''abcd''
) que utilizan algunos analizadores en tiempo de compilació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.
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.
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 <char... chars>
using tstring = std::integer_sequence<char, chars...>;
Crear un operador literal definido por el usuario:
template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }
Y use especialización parcial para recuperar los datos del personaje según sea necesario:
template <typename>
struct X;
template <char... elements>
struct X<tstring<elements...>> {
const char* GetString() const
{
static constexpr char str[sizeof...(elements) + 1] = { elements..., ''/0'' };
return str;
}
};
Esto te permite escribir:
X<decltype("my_string"_tstr)>
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 compilaciones recientes de GCC y Clang, y con suerte se reconsiderará para C ++ 1z.
Puede usar la dirección de la cadena con enlace externo como un parámetro de plantilla, por ejemplo:
template <const char** T> 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 );
}
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<char ...c>
) y es posible pasar una cadena literal a dicha plantilla.
Esto funcionaría, sin embargo:
template <char const *str>
struct X
{
const char *GetString() const
{
return str;
}
};
char global_string[] = "String";
int main()
{
X<global_string> x;
cout<<x.GetString();
}