programas - sintaxis de c++ definicion
cómo saber si un tipo de plantilla de C++ es una cadena de estilo C (5)
Desea verificar si el tipo es el mismo que un char *
, pero está negando el resultado de std::is_same
, que claramente no va a producir el resultado correcto. Así que vamos a eliminar eso.
template<class T>
struct is_c_str
: std::integral_constant<
bool,
std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};
Sin embargo, esto ahora dará como resultado la salida 0, 0, 0
. El problema ahora es que remove_cv
elimina los calificadores cv de nivel superior, pero la const
en char const *
no es de nivel superior.
Si desea hacer coincidir tanto char *
como char const *
la solución más sencilla es:
template<class T>
struct is_c_str
: std::integral_constant<
bool,
std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value ||
std::is_same<char const *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};
La versión anterior aún no coincidirá con char[]
. Si también desea hacer coincidir esos, y reducir la verbosidad de combinar std::remove_reference
y std::remove_cv
, use std::decay
lugar.
template<class T>
struct is_c_str
: std::integral_constant<
bool,
std::is_same<char const *, typename std::decay<T>::type>::value ||
std::is_same<char *, typename std::decay<T>::type>::value
> {};
Estoy tratando de escribir una plantilla is_c_str
para probar si un tipo es una cadena de estilo c. Necesito esto como un intento de escribir una función to_string, como se muestra en mi otra pregunta aquí: ¿ Especialización de plantillas para iteradores de contenedores STL? .
Necesito distinguir c_str y otros tipos de punteros e iteradores, para poder representar el primero en el valor nominal, y representar los punteros / iteradores como un "itor" o "ptr" opaco. El código es el siguiente:
#include <iostream>
template<class T>
struct is_c_str
: std::integral_constant<
bool,
!std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};
int main() {
auto sz = "Hello"; //Or: const char * sz = "Hello";
int i;
double d;
std::cout << is_c_str<decltype(sz)>::value << ", "
<< is_c_str<decltype(i)>::value << ", "
<< is_c_str<decltype(d)>::value << std::endl;
}
Sin embargo, is_c_str
captura no solo const char *
, sino también int
y double
. Las salidas del código anterior:
1, 1, 1
(a partir de gcc-4.8.1).
¿Mi pregunta es cómo arreglar is_c_str
para capturar correctamente las cadenas de estilo c?
El tipo de sz
es char const*
, pero std::remove_cv<>
solo elimina la const
nivel superior, por lo que no puede obtener char*
través de su aplicación. En su lugar, simplemente puede buscar una char const*
después de descomponer completamente el tipo con std::decay<>
:
namespace detail
{
template<class T>
struct is_c_str : std::is_same<char const*, T> {};
}
template<class T>
struct is_c_str : detail::is_c_str<typename std::decay<T>::type> {};
int main() {
auto sz = "Hello";
int i;
double d;
std::cout << is_c_str<decltype(sz)>::value << ", "
<< is_c_str<decltype(i)>::value << ", "
<< is_c_str<decltype(d)>::value << std::endl;
}
También estabas negando falsamente la condición. Yo también arreglé eso.
Hay un par de problemas.
La línea
!std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
necesita ser
std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
Su lógica de usar
typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
es defectuosa. No conviertechar const*
achar*
. Puede convertirchar* const
achar*
.
Lo que necesitas es:
template<class T>
struct is_c_str
: std::integral_constant<
bool,
std::is_same<char *, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value ||
std::is_same<char const*, typename std::remove_reference<typename std::remove_cv<T>::type>::type>::value
> {};
Intenté esto y parece funcionar:
#include <iostream>
template<class T>
struct is_c_str : std::integral_constant<bool, false> {};
template<>
struct is_c_str<char*> : std::integral_constant<bool, true> {};
template<>
struct is_c_str<const char*> : std::integral_constant<bool, true> {};
int main() {
auto sz = "Hello";
int i;
double d;
std::cout << is_c_str<decltype(sz)>::value << ", "
<< is_c_str<decltype(i)>::value << ", "
<< is_c_str<decltype(d)>::value << std::endl;
}
Obviamente, enumerar todos los casos no es tan elegante como poner el predicado general en std:integral_constant
, pero, por otro lado, ese predicado es una lengua extraña para idiotas como yo, mientras que la especialización de plantillas de "fuerza bruta" es algo más comprensible y viable en este sentido. Caso como hay pocas especializaciones.
Ya hay algunas soluciones, pero como la solución más simple es realmente simple, la anotaré aquí.
template< typename, typename = void >
struct is_c_str
: std::false_type {};
template< typename t >
struct is_c_str< t *,
typename std::enable_if< std::is_same<
typename std::decay< t >::type,
char
>::value >::type
>
: std::true_type {};
La parte difícil, por supuesto, es analizar qué hay dentro del tipo de puntero en lugar del tipo de puntero en sí.