c++ - plantillas - templates en c
Pasar un literal de cadena como parĂ¡metro a una clase de plantilla de C++ (10)
Quiero una clase que tome dos parámetros en su constructor. El primero puede ser int, double o float, entonces, y el segundo es siempre un literal de cadena "my string"
template<typename T>
class demo
{
T data;
std::string s;
public:
demo(T d,std::string x="my string"):data(d),s(x) //Your constructor
{
}
};
No estoy seguro, pero ¿es esto algo lo que quieres?
Quiero una clase que tome dos parámetros en su constructor. El primero puede ser int, double o float, por lo que <typename T>
, y el segundo es siempre un literal de cadena "my string", así que supongo que const char * const
.
¿Alguien puede darme algún código compilable que declare una plantilla de clase simple como se describe y declare un objeto de esa clase?
Gracias
una cadena literal "mi cuerda", así que supongo que const char * const
En realidad, los literales de cadena con n caracteres visibles son del tipo const char[n+1]
.
#include <iostream>
#include <typeinfo>
template<class T>
void test(const T& t)
{
std::cout << typeid(t).name() << std::endl;
}
int main()
{
test("hello world"); // prints A12_c on my compiler
}
EDIT: ok, el título de tu pregunta parece ser engañoso
"Quiero una clase que tenga dos parámetros en su constructor. La primera puede ser int, double o float, entonces, y la segunda es siempre una cadena literal" my string ", así que supongo que const char * const."
Parece que estás tratando de lograr:
template<typename T>
class Foo
{
public:
Foo(T t, const char* s) : first(t), second(s)
{
// do something
}
private:
T first;
const char* second;
};
Esto funcionaría para cualquier tipo, para el primer parámetro: int
, float
, double
, whatever.
Ahora, si realmente quiere restringir el tipo del primer parámetro para que sea solo int
, float
o double
; puedes encontrar algo más elaborado como
template<typename T>
struct RestrictType;
template<>
struct RestrictType<int>
{
typedef int Type;
};
template<>
struct RestrictType<float>
{
typedef float Type;
};
template<>
struct RestrictType<double>
{
typedef double Type;
};
template<typename T>
class Foo
{
typedef typename RestrictType<T>::Type FirstType;
public:
Foo(FirstType t, const char* s) : first(t), second(s)
{
// do something
}
private:
FirstType first;
const char* second;
};
int main()
{
Foo<int> f1(0, "can");
Foo<float> f2(1, "i");
Foo<double> f3(1, "have");
//Foo<char> f4(0, "a pony?");
}
Si elimina el comentario en la última línea, obtendrá efectivamente un error de compilación.
Los literales de cadena no son permitidos por C ++ 2003
ISO / IEC 14882-2003 §14.1:
14.1 Parámetros de la plantilla
Un parámetro de plantilla sin tipo debe tener uno de los siguientes tipos (opcionalmente calificado):
- tipo integral o de enumeración,
- puntero a objeto o puntero a función,
- referencia al objeto o referencia a la función,
- puntero a miembro.
ISO / IEC 14882-2003 §14.3.2:
14.3.2 Argumentos de plantilla sin tipo
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 ID de plantilla de función, pero excluyendo miembros de clase no estáticos, expresada como expresión & id 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.
[Nota: Un literal de cadena (2.13.4) no satisface los requisitos de ninguna de estas categorías y, por lo tanto, no es un argumento de plantilla aceptable.
[Ejemplo:
template<class T, char* p> class X {
//...
X();
X(const char* q) { /* ... */ }
};
X<int,"Studebaker"> x1; //error: string literal as template-argument
char p[] = "Vivisectionist";
X<int,p> x2; //OK
-end example] -finalizar nota]
Y parece que no va a cambiar en el C ++ 0X venidero, consulte el borrador actual 14.4.2 . Argumentos sin tipo de plantilla .
En base a sus comentarios bajo la respuesta de Niel, otra posibilidad es la siguiente:
#include <iostream>
static const char* eventNames[] = { "event_A", "event_B" };
enum EventId {
event_A = 0,
event_B
};
template <int EventId>
class Event
{
public:
Event() {
name_ = eventNames[EventId];
}
void print() {
std::cout << name_ << std::endl;
}
private:
const char* name_;
};
int main()
{
Event<event_A>().print();
Event<event_B>().print();
}
huellas dactilares
event_A
event_B
Esta es una solución con MPLLIBS para pasar cadenas como argumentos de plantilla (C ++ 11).
#include <iostream>
#include <mpllibs/metaparse/string.hpp> // https://github.com/sabel83/mpllibs
#include <boost/mpl/string.hpp>
// -std=c++11
template<class a_mpl_string>
struct A
{
static const char* string;
};
template<class a_mpl_string>
const char* A< a_mpl_string >
::string { boost::mpl::c_str< a_mpl_string >::value }; // boost compatible
typedef A< MPLLIBS_STRING ( "any string as template argument" ) > a_string_type;
int main ( int argc, char **argv )
{
std::cout << a_string_type{}.string << std::endl;
return 0;
}
huellas dactilares:
any string as template argument
La lib en github: https://github.com/sabel83/mpllibs
Lo sentimos, C ++ actualmente no admite el uso de literales de cadena (o literales reales) como parámetros de plantilla.
Pero volver a leer su pregunta, ¿es eso lo que está preguntando? No puedes decir:
foo <"bar"> x;
pero puedes decir
template <typename T>
struct foo {
foo( T t ) {}
};
foo <const char *> f( "bar" );
Más allá de la respuesta de Neil: una forma de usar cadenas con plantillas como lo desea es definir una clase de rasgos y definir la cadena como un rasgo del tipo.
#include <iostream>
template <class T>
struct MyTypeTraits
{
static const char* name;
};
template <class T>
const char* MyTypeTraits<T>::name = "Hello";
template <>
struct MyTypeTraits<int>
{
static const char* name;
};
const char* MyTypeTraits<int>::name = "Hello int";
template <class T>
class MyTemplateClass
{
public:
void print() {
std::cout << "My name is: " << MyTypeTraits<T>::name << std::endl;
}
};
int main()
{
MyTemplateClass<int>().print();
MyTemplateClass<char>().print();
}
huellas dactilares
My name is: Hello int
My name is: Hello
No puede pasar un literal de cadena directamente como un parámetro de plantilla.
Pero puedes acercarte:
template<class MyString = typestring_is("Hello!")>
void MyPrint() {
puts( MyString::data() );
}
...
// or:
MyPrint<typestring_is("another text")>();
...
Todo lo que necesita es un pequeño archivo de encabezado desde here .
Alternativas:
Defina una
char const *
global dechar const *
y páselo a la plantilla como puntero. ( here )Drawback: requiere código adicional fuera de la lista de argumentos de la plantilla. No es adecuado si necesita especificar el literal de cadena "en línea".
Use una extensión de idioma no estándar. ( here )
Drawback: no se garantiza que funcione con todos los compiladores.
Use
BOOST_METAPARSE_STRING
. ( here )Drawback: su código dependerá de la biblioteca de Boost.
Utilice un paquete de parámetros de plantilla variadica de char, por ejemplo,
str_t<''T'',''e'',''s'',''t''>
.Esto es lo que la solución anterior hace por ti detrás de escena.
Puede tener un parámetro de plantilla const char*
non-type y pasarle una variable const char[]
con enlace static
, que no está tan lejos de pasar un literal de cadena directamente.
#include <iostream>
template<const char *str>
struct cts {
void p() {std::cout << str;}
};
static const char teststr[] = "Hello world!";
int main() {
cts<teststr> o;
o.p();
}
inline const wchar_t *GetTheStringYouWant() { return L"The String You Want"; }
template <const wchar_t *GetLiteralFunc(void)>
class MyType
{
void test()
{
std::cout << GetLiteralFunc;
}
}
int main()
{
MyType<GetTheStringYouWant>.test();
}
Pruébelo pegando la dirección de una función como argumento de la plantilla.