Aqui está um resumo da solução de longo formulário de Howard, mas implementado com um de uma linha macro herética: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Se você precisar de suporte multi-plataforma: Use #ifdef, #else, #endifpara fornecer um macros para outras plataformas como MSVC.
Se você usar isso apenas para depuração, poderá considerar template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Em seguida, usar eg print_T<const int * const **>();imprimirá void print_T() [T = const int *const **]em tempo de execução e preservará todos os qualificadores (funciona no GCC e no Clang).
Henri Menke
@ Henri, __PRETTY_FUNCTION__não é padrão em C ++ (o requisito está no título da pergunta).
Toby Speight
Respostas:
505
Atualização do C ++ 11 para uma pergunta muito antiga: imprima o tipo de variável em C ++.
A resposta aceita (e boa) é usar typeid(a).name(), onde aé um nome de variável.
Agora, em C ++ 11, temos decltype(x), que pode transformar uma expressão em um tipo. E decltype()vem com seu próprio conjunto de regras muito interessantes. Por exemplo, decltype(a)e decltype((a))geralmente serão tipos diferentes (e por razões boas e compreensíveis quando essas razões forem expostas).
Nossos confiáveis typeid(a).name()nos ajudarão a explorar este admirável mundo novo?
Não.
Mas a ferramenta que a vontade não é tão complicada. E é essa ferramenta que estou usando como resposta a esta pergunta. Vou comparar e contrastar esta nova ferramenta typeid(a).name(). E essa nova ferramenta é realmente construída sobre typeid(a).name().
A questão fundamental:
typeid(a).name()
joga fora qualificadores-cv, referências e lvalue / rvalue-ness. Por exemplo:
constint ci =0;
std::cout <<typeid(ci).name()<<'\n';
Para mim saídas:
i
e eu estou supondo em saídas MSVC:
int
Ou seja, o constse foi. Este não é um problema de QOI (Qualidade de implementação). O padrão exige esse comportamento.
O que eu estou recomendando abaixo é:
template<typename T> std::string type_name();
que seria usado assim:
constint ci =0;
std::cout << type_name<decltype(ci)>()<<'\n';
e para mim saídas:
intconst
<disclaimer>Eu não testei isso no MSVC. </disclaimer> Mas agradeço o feedback daqueles que o fazem.
A solução C ++ 11
Estou usando __cxa_demanglepara plataformas não-MSVC, como recomendado pelo ipapadop em sua resposta para desmembrar tipos. Mas no MSVC, estou confiando typeidem desmantelar nomes (não testados). E esse núcleo envolve alguns testes simples que detectam, restauram e reportam qualificadores de cv e referências ao tipo de entrada.
#include<type_traits>#include<typeinfo>#ifndef _MSC_VER
# include <cxxabi.h>#endif#include<memory>#include<string>#include<cstdlib>template<class T>
std::string
type_name(){typedeftypename std::remove_reference<T>::type TR;
std::unique_ptr<char,void(*)(void*)> own
(#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(),nullptr,nullptr,nullptr),#elsenullptr,#endif
std::free
);
std::string r = own !=nullptr? own.get():typeid(TR).name();if(std::is_const<TR>::value)
r +=" const";if(std::is_volatile<TR>::value)
r +=" volatile";if(std::is_lvalue_reference<T>::value)
r +="&";elseif(std::is_rvalue_reference<T>::value)
r +="&&";return r;}
Os resultados
Com esta solução, posso fazer o seguinte:
int& foo_lref();int&& foo_rref();int foo_value();int
main(){int i =0;constint ci =0;
std::cout <<"decltype(i) is "<< type_name<decltype(i)>()<<'\n';
std::cout <<"decltype((i)) is "<< type_name<decltype((i))>()<<'\n';
std::cout <<"decltype(ci) is "<< type_name<decltype(ci)>()<<'\n';
std::cout <<"decltype((ci)) is "<< type_name<decltype((ci))>()<<'\n';
std::cout <<"decltype(static_cast<int&>(i)) is "<< type_name<decltype(static_cast<int&>(i))>()<<'\n';
std::cout <<"decltype(static_cast<int&&>(i)) is "<< type_name<decltype(static_cast<int&&>(i))>()<<'\n';
std::cout <<"decltype(static_cast<int>(i)) is "<< type_name<decltype(static_cast<int>(i))>()<<'\n';
std::cout <<"decltype(foo_lref()) is "<< type_name<decltype(foo_lref())>()<<'\n';
std::cout <<"decltype(foo_rref()) is "<< type_name<decltype(foo_rref())>()<<'\n';
std::cout <<"decltype(foo_value()) is "<< type_name<decltype(foo_value())>()<<'\n';}
e a saída é:
decltype(i) is intdecltype((i)) is int&decltype(ci) is intconstdecltype((ci)) is intconst&decltype(static_cast<int&>(i)) is int&decltype(static_cast<int&&>(i)) is int&&decltype(static_cast<int>(i)) is intdecltype(foo_lref()) is int&decltype(foo_rref()) is int&&decltype(foo_value()) is int
Observe (por exemplo) a diferença entre decltype(i)e decltype((i)). O primeiro é o tipo da declaração de i. O último é o "tipo" da expressãoi . (as expressões nunca têm tipo de referência, mas, como convenção, decltyperepresentam expressões lvalue com referências lvalue).
Portanto, essa ferramenta é um excelente veículo apenas para aprender decltype, além de explorar e depurar seu próprio código.
Por outro lado, se eu fosse construir isso apenas typeid(a).name(), sem adicionar novamente qualificadores ou referências cv perdidos, a saída seria:
decltype(i) is intdecltype((i)) is intdecltype(ci) is intdecltype((ci)) is intdecltype(static_cast<int&>(i)) is intdecltype(static_cast<int&&>(i)) is intdecltype(static_cast<int>(i)) is intdecltype(foo_lref()) is intdecltype(foo_rref()) is intdecltype(foo_value()) is int
Ou seja, todas as referências e qualificadores de cv são retirados.
Atualização C ++ 14
Apenas quando você pensa que tem uma solução para um problema pregado, alguém sempre sai do nada e mostra uma maneira muito melhor. :-)
Esta resposta do Jamboree mostra como obter o nome do tipo em C ++ 14 em tempo de compilação. É uma solução brilhante por alguns motivos:
Está em tempo de compilação!
Você solicita que o compilador faça o trabalho em vez de uma biblioteca (mesmo um std :: lib). Isso significa resultados mais precisos para os recursos mais recentes do idioma (como lambdas).
A resposta de Jamboree não explica tudo para o VS, e estou ajustando um pouco o código dele. Mas como essa resposta recebe muitas visualizações, reserve um tempo para ir até lá e aprovar sua resposta, sem a qual, essa atualização nunca teria acontecido.
Esse código será retirado automaticamente constexprse você ainda estiver preso no antigo C ++ 11. E se você estiver pintando na parede da caverna com C ++ 98/03, o noexceptsacrifício também será.
Atualização C ++ 17
Nos comentários abaixo, Lyberta ressalta que o novo std::string_viewpode substituir static_string:
template<class T>constexpr
std::string_view
type_name(){usingnamespace std;#ifdef __clang__
string_view p = __PRETTY_FUNCTION__;return string_view(p.data()+34, p.size()-34-1);#elif defined(__GNUC__)
string_view p = __PRETTY_FUNCTION__;# if __cplusplus < 201402return string_view(p.data()+36, p.size()-36-1);# elsereturn string_view(p.data()+49, p.find(';',49)-49);# endif#elif defined(_MSC_VER)
string_view p = __FUNCSIG__;return string_view(p.data()+84, p.size()-84-7);#endif}
Atualizei as constantes do VS graças ao excelente trabalho de detetive de Jive Dadson nos comentários abaixo.
Atualizar:
Certifique-se de verificar esta reescrita abaixo, que elimina os números mágicos ilegíveis na minha formulação mais recente.
O VS 14 CTP imprimiu os tipos corretos, só tive que adicionar uma #include <iostream>linha.
precisa
3
Por que modelo <typename T> std :: string type_name ()? Por que você não está passando um tipo como argumento?
moonman239
2
Acredito que minha lógica era que, às vezes, eu só tinha um tipo (como um parâmetro de modelo deduzido) e não queria ter que construir artificialmente um deles para obter o tipo (embora esses dias declvalfuncionem bem).
Howard Hinnant
5
@AngelusMortis: como o inglês é vago / ambíguo em comparação com o código C ++, encorajo você a copiar / colar isso no seu caso de teste com o tipo específico de seu interesse e com o compilador específico de seu interesse, e escreva novamente com mais detalhes se o resultado for surpreendente e / ou insatisfatório.
Howard Hinnant 02/03
3
@HowardHinnant você pode usar em std::string_viewvez de static_string?
Talvez você precise ativar o RTTI nas opções do compilador para que isso funcione. Além disso, a saída disso depende do compilador. Pode ser um nome de tipo bruto ou um símbolo de distorção de nome ou qualquer outra coisa.
Por que a string retornada pela função name () é definida como implementação?
Destructor
4
@PravasiMeet Não há uma boa razão, tanto quanto eu sei. O comitê simplesmente não queria forçar os implementadores de compiladores a seguir instruções técnicas específicas - provavelmente um erro, em retrospectiva.
21978 Konrad Rudolph
2
Existe um sinalizador que eu possa usar para ativar o RTTI? Talvez você possa tornar sua resposta inclusiva.
Jim Jim
4
@Destructor Fornecer um formato padronizado de manipulação de nomes pode dar a impressão de que a interoperabilidade entre binários criados por dois compiladores diferentes é possível e / ou segura, quando não é. Como o C ++ não possui uma ABI padrão, um esquema de confusão de nomes padrão seria inútil e potencialmente enganoso e perigoso.
Elkvis 5/05
11
@ Jim A seção sobre sinalizadores do compilador seria uma ordem de magnitude maior que a própria resposta. O GCC compila com ele por padrão, portanto, "-fno-rtti", outros compiladores podem optar por não fazê-lo, mas não há padrão para sinalizadores de compilador.
Kfsone #
82
Muito feio, mas faz o truque se você quiser apenas informações de tempo de compilação (por exemplo, para depuração):
auto testVar = std::make_tuple(1,1.0,"abc");decltype(testVar)::foo=1;
Devoluções:
Compilation finished with errors:
source.cpp:In function 'int main()':
source.cpp:5:19: error:'foo' is not a member of 'std::tuple<int, double, const char*>'
somente o c ++ poderia tornar isso tão difícil (imprimir um tipo de variáveis automáticas em tempo de compilação). SOMENTE C ++.
Karl Pickett
3
@KarlP bem para ser justo, é um pouco complicado, isso funciona também :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV
No VC ++ 17, isso reduz uma referência rvalue a uma referência simples, mesmo em uma função de modelo com o parâmetro forwarding-reference, e o nome do objeto envolvido em std :: forward.
precisa
Você foi capaz de chegar ao tipo sem criar novas rodas!
Steven Eckhoff
11
Esta técnica é também descrita no "Item 4: Saiba como visualizar tipos deduzidos" em Effective Modern C ++
lenkite
54
Não esqueça de incluir <typeinfo>
Acredito que você está se referindo à identificação do tipo de tempo de execução. Você pode conseguir o acima, fazendo.
Portanto, você não pode usar essas informações para serialização. Mas ainda assim, a propriedade typeid (a) .name () ainda pode ser usada para fins de log / depuração
Não imprimirá "int" para shorts e chars? E "flutuar" para duplas?
gartenriese 17/02
11
A especialização @gartenriese não tem essa desvantagem. Para doubleisso seria compilar a versão não-especializado da função de modelo em vez de fazer uma conversão implícita de tipo de utilizar a especialização: cpp.sh/2wzc
chappjc
11
@chappjc: Sinceramente, não sei por que perguntei naquela época, agora está bem claro para mim. Mas obrigado por responder a uma pergunta de um ano!
gartenriese
2
@gartenriese eu imaginei isso, mas "a internet" pode ter a mesma pergunta em algum momento.
Chappjc 5/03/2015
18
Como mencionado, typeid().name()pode retornar um nome mutilado. No GCC (e em alguns outros compiladores), você pode contornar o problema com o seguinte código:
#include<cxxabi.h>#include<iostream>#include<typeinfo>#include<cstdlib>namespace some_namespace {namespace another_namespace {class my_class {};}}int main(){typedef some_namespace::another_namespace::my_class my_type;// mangled
std::cout <<typeid(my_type).name()<< std::endl;// unmangledint status =0;char* demangled = abi::__cxa_demangle(typeid(my_type).name(),0,0,&status);switch(status){case-1:{// could not allocate memory
std::cout <<"Could not allocate memory"<< std::endl;return-1;}break;case-2:{// invalid name under the C++ ABI mangling rules
std::cout <<"Invalid name"<< std::endl;return-1;}break;case-3:{// invalid argument
std::cout <<"Invalid argument to demangle()"<< std::endl;return-1;}break;}
std::cout << demangled << std::endl;
free(demangled);return0;
o DECLARE_TYPE_NAME definição existe para facilitar sua vida na declaração dessa classe de características para todos os tipos que você espera.
Isso pode ser mais útil do que as soluções que envolvem, typeidporque você controla a saída. Por exemplo, usar typeidfor long longno meu compilador fornece "x".
No C ++ 11, temos decltype. Não há como no c ++ padrão exibir o tipo exato de variável declarada usando decltype. Podemos usar o tipo de impulso index type_id_with_cvr( ie cvr significa const, volatile, reference) para imprimir o tipo como abaixo.
#include<iostream>#include<boost/type_index.hpp>usingnamespace std;using boost::typeindex::type_id_with_cvr;int main(){int i =0;constint ci =0;
cout <<"decltype(i) is "<< type_id_with_cvr<decltype(i)>().pretty_name()<<'\n';
cout <<"decltype((i)) is "<< type_id_with_cvr<decltype((i))>().pretty_name()<<'\n';
cout <<"decltype(ci) is "<< type_id_with_cvr<decltype(ci)>().pretty_name()<<'\n';
cout <<"decltype((ci)) is "<< type_id_with_cvr<decltype((ci))>().pretty_name()<<'\n';
cout <<"decltype(std::move(i)) is "<< type_id_with_cvr<decltype(std::move(i))>().pretty_name()<<'\n';
cout <<"decltype(std::static_cast<int&&>(i)) is "<< type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name()<<'\n';return0;}
seria mais simples usar uma função auxiliar:template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
r0ng 18/17
6
Você também pode usar o c ++ filt com a opção -t (type) para desmontar o nome do tipo:
Inferno feio, mas vai fazer o que eu preciso. E muito menor que as outras soluções. Funciona em Mac btw.
Marco Luglio 20/05
6
Howard Hinnant usou números mágicos para extrair o nome do tipo. Pref 桓 瑋 sugeriu prefixo e sufixo de string. Mas prefixo / sufixo continuam mudando. Com "probe_type" type_name calcula automaticamente os tamanhos de prefixo e sufixo para "probe_type" para extrair o nome do tipo:
test
constint*&unsignedintconstintconstint*constint*&constexpr std::string_view type_name()[with T = probe_type; std::string_view = std::basic_string_view<char>]
clang 10.0.0 Wandbox:
test
constint*&unsignedintconstintconstint*constint*&
std::__1::string_view type_name()[T = probe_type]
VS 2019 versão 16.3.3:
class test
constint*&unsignedintconstintconstint*constint*&class std::basic_string_view<char,struct std::char_traits<char>> __cdecl type_name<class probe_type>(void)
As outras respostas que envolvem RTTI (typeid) são provavelmente o que você deseja, desde que:
você pode pagar a sobrecarga de memória (o que pode ser considerável em alguns compiladores)
os nomes de classe que seu compilador retorna são úteis
A alternativa (semelhante à resposta de Greg Hewgill) é criar uma tabela de características em tempo de compilação.
template<typename T>struct type_as_string;// declare your Wibble type (probably with definition of Wibble)template<>struct type_as_string<Wibble>{staticconstchar*const value ="Wibble";};
Esteja ciente de que, se você agrupar as declarações em uma macro, terá problemas ao declarar nomes para os tipos de modelo usando mais de um parâmetro (por exemplo, std :: map), devido à vírgula.
Para acessar o nome do tipo de uma variável, tudo o que você precisa é
(i) não funcionará para outros tipos (ou seja, não é genérico); (ii) inchaço inútil do código; (iii) o mesmo pode ser (corretamente) feito com typeidou decltype.
Edmz 13/03/2015
2
Você está certo, mas abrange todos os tipos básicos ... e isso é o que eu preciso agora ..
Jahid
2
Você pode me dizer, como você faria com decltype,
Jahid
11
Se é um teste de tempo de compilação, você pode usar std :: is_same <T, S> e decltype para obter T e S.
edmz
4
Ao desafiar, decidi testar até que ponto alguém pode ir com truques de modelos independentes de plataforma (espero).
Os nomes são montados completamente no momento da compilação. (O que significa typeid(T).name()que não pôde ser usado, portanto, você deve fornecer explicitamente nomes para tipos não compostos. Caso contrário, os espaços reservados serão exibidos.)
Exemplo de uso:
TYPE_NAME(int)
TYPE_NAME(void)// You probably should list all primitive types here.
TYPE_NAME(std::string)int main(){// A simple case
std::cout << type_name<void(*)(int)><<'\n';// -> `void (*)(int)`// Ugly mess case// Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
std::cout << type_name<void(std::string::*(int[3],constint,void(*)(std::string)))(volatileint*const*)><<'\n';// -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`// A case with undefined types// If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
std::cout << type_name<std::ostream (*)(int,short)><<'\n';// -> `class? (*)(int,??)`// With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.}
Código:
#include<type_traits>#include<utility>staticconstexpr std::size_t max_str_lit_len =256;template<std::size_t I, std::size_t N>constexprchar sl_at(constchar(&str)[N]){ifconstexpr(I < N)return str[I];elsereturn'\0';}constexpr std::size_t sl_len(constchar*str){for(std::size_t i =0; i < max_str_lit_len; i++)if(str[i]=='\0')return i;return0;}template<char...C>struct str_lit
{staticconstexprchar value[]{C...,'\0'};staticconstexprint size = sl_len(value);template<typename F,typename...P>struct concat_impl {using type =typename concat_impl<F>::type::template concat_impl<P...>::type;};template<char...CC>struct concat_impl<str_lit<CC...>>{using type = str_lit<C..., CC...>;};template<typename...P>using concat =typename concat_impl<P...>::type;};template<typename,constchar*>struct trim_str_lit_impl;template<std::size_t...I,constchar*S>struct trim_str_lit_impl<std::index_sequence<I...>, S>{using type = str_lit<S[I]...>;};template<std::size_t N,constchar*S>using trim_str_lit =typename trim_str_lit_impl<std::make_index_sequence<N>, S>::type;#define STR_LIT(str)::trim_str_lit<::sl_len(str),::str_lit<STR_TO_VA(str)>::value>#define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)#define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)#define STR_TO_VA_4(str,off)::sl_at<off+0>(str),::sl_at<off+1>(str),::sl_at<off+2>(str),::sl_at<off+3>(str)template<char...C>constexpr str_lit<C...> make_str_lit(str_lit<C...>){return{};}template<std::size_t N>constexprauto make_str_lit(constchar(&str)[N]){return trim_str_lit<sl_len((constchar(&)[N])str), str>{};}template<std::size_t A, std::size_t B>struct cexpr_pow {staticconstexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};template<std::size_t A>struct cexpr_pow<A,0>{staticconstexpr std::size_t value =1;};template<std::size_t N, std::size_t X,typename= std::make_index_sequence<X>>struct num_to_str_lit_impl;template<std::size_t N, std::size_t X, std::size_t...Seq>struct num_to_str_lit_impl<N, X, std::index_sequence<Seq...>>{staticconstexprauto func(){ifconstexpr(N >= cexpr_pow<10,X>::value)return num_to_str_lit_impl<N, X+1>::func();elsereturn str_lit<(N / cexpr_pow<10,X-1-Seq>::value %10+'0')...>{};}};template<std::size_t N>using num_to_str_lit =decltype(num_to_str_lit_impl<N,1>::func());using spa = str_lit<' '>;using lpa = str_lit<'('>;using rpa = str_lit<')'>;using lbr = str_lit<'['>;using rbr = str_lit<']'>;using ast = str_lit<'*'>;using amp = str_lit<'&'>;using con = str_lit<'c','o','n','s','t'>;using vol = str_lit<'v','o','l','a','t','i','l','e'>;using con_vol = con::concat<spa, vol>;using nsp = str_lit<':',':'>;using com = str_lit<','>;using unk = str_lit<'?','?'>;using c_cla = str_lit<'c','l','a','s','s','?'>;using c_uni = str_lit<'u','n','i','o','n','?'>;using c_enu = str_lit<'e','n','u','m','?'>;template<typename T>inlineconstexprbool ptr_or_ref = std::is_pointer_v<T>|| std::is_reference_v<T>|| std::is_member_pointer_v<T>;template<typename T>inlineconstexprbool func_or_arr = std::is_function_v<T>|| std::is_array_v<T>;template<typename T>struct primitive_type_name {using value = unk;};template<typename T,typename= std::enable_if_t<std::is_class_v<T>>>using enable_if_class = T;template<typename T,typename= std::enable_if_t<std::is_union_v<T>>>using enable_if_union = T;template<typename T,typename= std::enable_if_t<std::is_enum_v <T>>>using enable_if_enum = T;template<typename T>struct primitive_type_name<enable_if_class<T>>{using value = c_cla;};template<typename T>struct primitive_type_name<enable_if_union<T>>{using value = c_uni;};template<typename T>struct primitive_type_name<enable_if_enum <T>>{using value = c_enu;};template<typename T>struct type_name_impl;template<typename T>using type_name_lit = std::conditional_t<std::is_same_v<typename primitive_type_name<T>::value::template concat<spa>,typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>,typename primitive_type_name<T>::value,typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>;template<typename T>inlineconstexprconstchar*type_name = type_name_lit<T>::value;template<typename T,typename= std::enable_if_t<!std::is_const_v<T>&&!std::is_volatile_v<T>>>using enable_if_no_cv = T;template<typename T>struct type_name_impl
{using l =typename primitive_type_name<T>::value::template concat<spa>;using r = str_lit<>;};template<typename T>struct type_name_impl<const T>{using new_T_l = std::conditional_t<type_name_impl<T>::l::size &&!ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,typename type_name_impl<T>::l>;using l = std::conditional_t<ptr_or_ref<T>,typename new_T_l::template concat<con>,
con::concat<new_T_l>>;using r =typename type_name_impl<T>::r;};template<typename T>struct type_name_impl<volatile T>{using new_T_l = std::conditional_t<type_name_impl<T>::l::size &&!ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,typename type_name_impl<T>::l>;using l = std::conditional_t<ptr_or_ref<T>,typename new_T_l::template concat<vol>,
vol::concat<new_T_l>>;using r =typename type_name_impl<T>::r;};template<typename T>struct type_name_impl<constvolatile T>{using new_T_l = std::conditional_t<type_name_impl<T>::l::size &&!ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,typename type_name_impl<T>::l>;using l = std::conditional_t<ptr_or_ref<T>,typename new_T_l::template concat<con_vol>,
con_vol::concat<new_T_l>>;using r =typename type_name_impl<T>::r;};template<typename T>struct type_name_impl<T *>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, ast>,typename type_name_impl<T>::l::template concat< ast>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<T &>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, amp>,typename type_name_impl<T>::l::template concat< amp>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<T &&>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, amp, amp>,typename type_name_impl<T>::l::template concat< amp, amp>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T,typename C>struct type_name_impl<T C::*>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, type_name_lit<C>, nsp, ast>,typename type_name_impl<T>::l::template concat< type_name_lit<C>, nsp, ast>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<enable_if_no_cv<T[]>>{using l =typename type_name_impl<T>::l;using r = lbr::concat<rbr,typename type_name_impl<T>::r>;};template<typename T, std::size_t N>struct type_name_impl<enable_if_no_cv<T[N]>>{using l =typename type_name_impl<T>::l;using r = lbr::concat<num_to_str_lit<N>, rbr,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<T()>{using l =typename type_name_impl<T>::l;using r = lpa::concat<rpa,typename type_name_impl<T>::r>;};template<typename T,typename P1,typename...P>struct type_name_impl<T(P1, P...)>{using l =typename type_name_impl<T>::l;using r = lpa::concat<type_name_lit<P1>,
com::concat<type_name_lit<P>>..., rpa,typename type_name_impl<T>::r>;};#define TYPE_NAME(t)template<>struct primitive_type_name<t>{using value = STR_LIT(#t);};
#include<iostream>#include<typeinfo>usingnamespace std;#define show_type_name(_t) \
system(("echo "+ string(typeid(_t).name())+" | c++filt -t").c_str())int main(){auto a ={"one","two","three"};
cout <<"Type of a: "<<typeid(a).name()<< endl;
cout <<"Real type of a:\n";
show_type_name(a);for(auto s : a){if(string(s)=="one"){
cout <<"Type of s: "<<typeid(s).name()<< endl;
cout <<"Real type of s:\n";
show_type_name(s);}
cout << s << endl;}int i =5;
cout <<"Type of i: "<<typeid(i).name()<< endl;
cout <<"Real type of i:\n";
show_type_name(i);return0;}
Resultado:
Type of a:St16initializer_listIPKcEReal type of a:
std::initializer_list<charconst*>Type of s:PKcReal type of s:charconst*
one
two
three
Type of i: i
Real type of i:int
Conforme explicado por Scott Meyers no Effective Modern C ++,
Chamadas para std::type_info::namenão são garantidos para retornar anythong sensata.
A melhor solução é permitir que o compilador gere uma mensagem de erro durante a dedução de tipo, por exemplo,
template<typename T>class TD;int main(){constint theAnswer =32;auto x = theAnswer;auto y =&theAnswer;
TD<decltype(x)> xType;
TD<decltype(y)> yType;return0;}
O resultado será algo assim, dependendo dos compiladores,
test4.cpp:10:21: error: aggregate ‘TD<int> xType’ has incomplete type and cannot be defined TD<decltype(x)> xType;
test4.cpp:11:21: error: aggregate ‘TD<constint*> yType’ has incomplete type and cannot be defined TD<decltype(y)> yType;
Por isso, sabemos que esse xé o tipo int, yé o tipo éconst int*
Para quem ainda está visitando, recentemente tive o mesmo problema e decidi escrever uma pequena biblioteca com base nas respostas deste post. Ele fornece nomes de tipo constexpr e índices de tipo e é testado no Mac, Windows e Ubuntu.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Se você precisar de suporte multi-plataforma: Use#ifdef
,#else
,#endif
para fornecer um macros para outras plataformas como MSVC.template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Em seguida, usar egprint_T<const int * const **>();
imprimirávoid print_T() [T = const int *const **]
em tempo de execução e preservará todos os qualificadores (funciona no GCC e no Clang).__PRETTY_FUNCTION__
não é padrão em C ++ (o requisito está no título da pergunta).Respostas:
Atualização do C ++ 11 para uma pergunta muito antiga: imprima o tipo de variável em C ++.
A resposta aceita (e boa) é usar
typeid(a).name()
, ondea
é um nome de variável.Agora, em C ++ 11, temos
decltype(x)
, que pode transformar uma expressão em um tipo. Edecltype()
vem com seu próprio conjunto de regras muito interessantes. Por exemplo,decltype(a)
edecltype((a))
geralmente serão tipos diferentes (e por razões boas e compreensíveis quando essas razões forem expostas).Nossos confiáveis
typeid(a).name()
nos ajudarão a explorar este admirável mundo novo?Não.
Mas a ferramenta que a vontade não é tão complicada. E é essa ferramenta que estou usando como resposta a esta pergunta. Vou comparar e contrastar esta nova ferramenta
typeid(a).name()
. E essa nova ferramenta é realmente construída sobretypeid(a).name()
.A questão fundamental:
joga fora qualificadores-cv, referências e lvalue / rvalue-ness. Por exemplo:
Para mim saídas:
e eu estou supondo em saídas MSVC:
Ou seja, o
const
se foi. Este não é um problema de QOI (Qualidade de implementação). O padrão exige esse comportamento.O que eu estou recomendando abaixo é:
que seria usado assim:
e para mim saídas:
<disclaimer>
Eu não testei isso no MSVC.</disclaimer>
Mas agradeço o feedback daqueles que o fazem.A solução C ++ 11
Estou usando
__cxa_demangle
para plataformas não-MSVC, como recomendado pelo ipapadop em sua resposta para desmembrar tipos. Mas no MSVC, estou confiandotypeid
em desmantelar nomes (não testados). E esse núcleo envolve alguns testes simples que detectam, restauram e reportam qualificadores de cv e referências ao tipo de entrada.Os resultados
Com esta solução, posso fazer o seguinte:
e a saída é:
Observe (por exemplo) a diferença entre
decltype(i)
edecltype((i))
. O primeiro é o tipo da declaração dei
. O último é o "tipo" da expressãoi
. (as expressões nunca têm tipo de referência, mas, como convenção,decltype
representam expressões lvalue com referências lvalue).Portanto, essa ferramenta é um excelente veículo apenas para aprender
decltype
, além de explorar e depurar seu próprio código.Por outro lado, se eu fosse construir isso apenas
typeid(a).name()
, sem adicionar novamente qualificadores ou referências cv perdidos, a saída seria:Ou seja, todas as referências e qualificadores de cv são retirados.
Atualização C ++ 14
Apenas quando você pensa que tem uma solução para um problema pregado, alguém sempre sai do nada e mostra uma maneira muito melhor. :-)
Esta resposta do Jamboree mostra como obter o nome do tipo em C ++ 14 em tempo de compilação. É uma solução brilhante por alguns motivos:
A resposta de Jamboree não explica tudo para o VS, e estou ajustando um pouco o código dele. Mas como essa resposta recebe muitas visualizações, reserve um tempo para ir até lá e aprovar sua resposta, sem a qual, essa atualização nunca teria acontecido.
Esse código será retirado automaticamente
constexpr
se você ainda estiver preso no antigo C ++ 11. E se você estiver pintando na parede da caverna com C ++ 98/03, onoexcept
sacrifício também será.Atualização C ++ 17
Nos comentários abaixo, Lyberta ressalta que o novo
std::string_view
pode substituirstatic_string
:Atualizei as constantes do VS graças ao excelente trabalho de detetive de Jive Dadson nos comentários abaixo.
Atualizar:
Certifique-se de verificar esta reescrita abaixo, que elimina os números mágicos ilegíveis na minha formulação mais recente.
fonte
#include <iostream>
linha.declval
funcionem bem).std::string_view
vez destatic_string
?Tentar:
Talvez você precise ativar o RTTI nas opções do compilador para que isso funcione. Além disso, a saída disso depende do compilador. Pode ser um nome de tipo bruto ou um símbolo de distorção de nome ou qualquer outra coisa.
fonte
Muito feio, mas faz o truque se você quiser apenas informações de tempo de compilação (por exemplo, para depuração):
Devoluções:
fonte
auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
Não esqueça de incluir
<typeinfo>
Acredito que você está se referindo à identificação do tipo de tempo de execução. Você pode conseguir o acima, fazendo.
fonte
De acordo com a solução de Howard , se você não quiser o número mágico, acho que essa é uma boa maneira de representar e parecer intuitiva:
fonte
Observe que os nomes gerados pelo recurso RTTI do C ++ não são portáveis. Por exemplo, a classe
terá os seguintes nomes:
Portanto, você não pode usar essas informações para serialização. Mas ainda assim, a propriedade typeid (a) .name () ainda pode ser usada para fins de log / depuração
fonte
Você pode usar modelos.
No exemplo acima, quando o tipo não for correspondido, será impresso "desconhecido".
fonte
double
isso seria compilar a versão não-especializado da função de modelo em vez de fazer uma conversão implícita de tipo de utilizar a especialização: cpp.sh/2wzcComo mencionado,
typeid().name()
pode retornar um nome mutilado. No GCC (e em alguns outros compiladores), você pode contornar o problema com o seguinte código:}
fonte
Você pode usar uma classe de características para isso. Algo como:
o
DECLARE_TYPE_NAME
definição existe para facilitar sua vida na declaração dessa classe de características para todos os tipos que você espera.Isso pode ser mais útil do que as soluções que envolvem,
typeid
porque você controla a saída. Por exemplo, usartypeid
forlong long
no meu compilador fornece "x".fonte
No C ++ 11, temos decltype. Não há como no c ++ padrão exibir o tipo exato de variável declarada usando decltype. Podemos usar o tipo de impulso index
type_id_with_cvr
( ie cvr significa const, volatile, reference) para imprimir o tipo como abaixo.fonte
template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
Você também pode usar o c ++ filt com a opção -t (type) para desmontar o nome do tipo:
Testado apenas no Linux.
fonte
Howard Hinnant usou números mágicos para extrair o nome do tipo. Pref 桓 瑋 sugeriu prefixo e sufixo de string. Mas prefixo / sufixo continuam mudando. Com "probe_type" type_name calcula automaticamente os tamanhos de prefixo e sufixo para "probe_type" para extrair o nome do tipo:
Resultado
gcc 10.0.0 20190919 Wandbox:
clang 10.0.0 Wandbox:
VS 2019 versão 16.3.3:
fonte
As outras respostas que envolvem RTTI (typeid) são provavelmente o que você deseja, desde que:
A alternativa (semelhante à resposta de Greg Hewgill) é criar uma tabela de características em tempo de compilação.
Esteja ciente de que, se você agrupar as declarações em uma macro, terá problemas ao declarar nomes para os tipos de modelo usando mais de um parâmetro (por exemplo, std :: map), devido à vírgula.
Para acessar o nome do tipo de uma variável, tudo o que você precisa é
fonte
Uma solução mais genérica sem sobrecarga de função que a anterior:
Aqui MyClass é uma classe definida pelo usuário. Mais condições podem ser adicionadas aqui também.
Exemplo:
Resultado:
fonte
Eu gosto do método de Nick. Um formulário completo pode ser este (para todos os tipos de dados básicos):
fonte
typeid
oudecltype
.Ao desafiar, decidi testar até que ponto alguém pode ir com truques de modelos independentes de plataforma (espero).
Os nomes são montados completamente no momento da compilação. (O que significa
typeid(T).name()
que não pôde ser usado, portanto, você deve fornecer explicitamente nomes para tipos não compostos. Caso contrário, os espaços reservados serão exibidos.)Exemplo de uso:
Código:
fonte
Resultado:
fonte
Conforme explicado por Scott Meyers no Effective Modern C ++,
A melhor solução é permitir que o compilador gere uma mensagem de erro durante a dedução de tipo, por exemplo,
O resultado será algo assim, dependendo dos compiladores,
Por isso, sabemos que esse
x
é o tipoint
,y
é o tipo éconst int*
fonte
Para quem ainda está visitando, recentemente tive o mesmo problema e decidi escrever uma pequena biblioteca com base nas respostas deste post. Ele fornece nomes de tipo constexpr e índices de tipo e é testado no Mac, Windows e Ubuntu.
O código da biblioteca está aqui: https://github.com/TheLartians/StaticTypeInfo
fonte