Вот краткое изложение длинных форм решения Говарда , но реализуется с еретической одной строки макроса: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Если вам нужна поддержка кросс-платформенной: Используйте #ifdef, #else, #endifчтобы предоставить один макрос для других платформ , таких как MSVC.
Если вы используете это только для отладки, вы можете рассмотреть template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Затем использование eg print_T<const int * const **>();будет печатать void print_T() [T = const int *const **]во время выполнения и сохраняет все квалификаторы (работает в GCC и Clang).
Анри Менке
@Henri, __PRETTY_FUNCTION__это не стандарт C ++ (требование в названии вопроса).
Тоби Спейт
Ответы:
505
Обновление C ++ 11 до очень старого вопроса: тип переменной печати в C ++.
Принятым (и хорошим) ответом является использование typeid(a).name(), где aимя переменной.
Теперь в C ++ 11 мы имеем decltype(x), который может превратить выражение в тип. И decltype()поставляется со своим набором очень интересных правил. Например, decltype(a)и decltype((a))обычно это будут разные типы (и по понятным и понятным причинам, когда эти причины будут выявлены).
Поможет ли наш верный typeid(a).name()нам исследовать этот дивный новый мир?
Нет.
Но инструмент, который будет не так сложен. И это тот инструмент, который я использую в качестве ответа на этот вопрос. Я буду сравнивать и сопоставлять этот новый инструмент typeid(a).name(). И этот новый инструмент на самом деле построен на вершине typeid(a).name().
Основная проблема:
typeid(a).name()
отбрасывает cv-квалификаторы, ссылки и lvalue / rvalue-ness. Например:
constint ci =0;
std::cout <<typeid(ci).name()<<'\n';
Для меня выводы:
i
и я предполагаю на выходах MSVC:
int
Т.е. constушел. Это не проблема QOI (Качество реализации). Стандарт обязывает это поведение.
Что я рекомендую ниже:
template<typename T> std::string type_name();
который будет использоваться следующим образом:
constint ci =0;
std::cout << type_name<decltype(ci)>()<<'\n';
и для меня выводы:
intconst
<disclaimer> Я не проверял это на MSVC. </disclaimer> Но я приветствую отзывы тех, кто это делает.
Решение C ++ 11
Я использую __cxa_demangleдля платформ не MSVC, как рекомендует ipapadop в своем ответе на demangle типов. Но на MSVC я typeidдоверяю разобрать имена (не проверено). И это ядро обернуто вокруг некоторого простого тестирования, которое обнаруживает, восстанавливает и сообщает cv-квалификаторы и ссылки на тип ввода.
#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;}
Результаты
С этим решением я могу сделать это:
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';}
и вывод:
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
Обратите внимание (например) на разницу между decltype(i)и decltype((i)). Прежний тип декларации о i. Последний является «типом» выраженияi . (выражения никогда не имеют ссылочного типа, но в качестве соглашения decltypeпредставляют выражения lvalue со ссылками lvalue).
Таким образом, этот инструмент является отличным средством для изучения decltype, в дополнение к изучению и отладке собственного кода.
Напротив, если бы я собирал это просто typeid(a).name(), без добавления потерянных cv-квалификаторов или ссылок, результат был бы:
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
Т.е. все ссылки и cv-квалификаторы удаляются.
C ++ 14 Обновление
Когда вы думаете, что у вас есть решение проблемы, кто-то прибыл, кто-то всегда появляется из ниоткуда и показывает вам гораздо лучший способ. :-)
Этот ответ от Jamboree показывает, как получить имя типа в C ++ 14 во время компиляции. Это блестящее решение по нескольким причинам:
Это во время компиляции!
Вы получаете сам компилятор для выполнения работы вместо библиотеки (даже std :: lib). Это означает более точные результаты для новейших языковых функций (таких как лямбды).
Ответ Джамбори не совсем раскрывает все для VS, и я немного подправил его код. Но так как этот ответ получает много просмотров, потратьте некоторое время, чтобы перейти к нему и высказать свой ответ, без которого это обновление никогда бы не произошло.
Этот код автоматически откатится, constexprесли вы все еще застряли в древнем C ++ 11. И если вы рисуете на стене пещеры с помощью C ++ 98/03,noexcept это тоже принесет в жертву.
C ++ 17 Обновление
В комментариях ниже Либерта указывает, что новое std::string_viewможет заменить 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}
Я обновил константы для VS благодаря очень хорошей детективной работе Джайва Дадсона в комментариях ниже.
Обновить:
Обязательно ознакомьтесь с этой перепиской ниже, которая устраняет нечитаемые магические числа в моей последней формулировке.
VS 14 CTP распечатал правильные типы, мне нужно было добавить только одну #include <iostream>строку.
Макс Галкин
3
Почему шаблон <typename T> std :: string type_name ()? Почему вы не передаете тип в качестве аргумента?
moonman239
2
Я полагаю, что мое обоснование заключалось в том, что иногда у меня был только тип (такой как выведенный параметр шаблона), и я не хотел искусственно создавать один из них, чтобы получить тип (хотя в наши дни это declvalможно сделать).
Говард Хиннант
5
@AngelusMortis: Поскольку английский является расплывчатым / неоднозначным по сравнению с кодом C ++, я рекомендую вам скопировать / вставить это в ваш тестовый набор с конкретным интересующим вас типом, а также с конкретным интересующим вас компилятором, и написать больше подробности, если результат удивителен и / или неудовлетворителен.
Говард Хиннант
3
@HowardHinnant вы можете использовать std::string_viewвместо static_string?
Возможно, вам придется активировать RTTI в опциях компилятора, чтобы это работало. Кроме того, вывод этого зависит от компилятора. Это может быть необработанное имя типа или символ искажения имени или что-то среднее между ними.
Почему строка, возвращаемая функцией name (), определяется реализацией?
Разрушитель
4
@PravasiMeet Нет веских причин, насколько я знаю. Комитет просто не хотел принуждать разработчиков компиляторов к конкретным техническим направлениям - возможно, ошибка в ретроспективе.
Конрад Рудольф
2
Есть ли флаг, который я мог бы использовать, чтобы включить RTTI? Может быть, вы могли бы сделать свой ответ включительно.
Джим
4
@Destructor Предоставление стандартизированного формата искажения имен может создать впечатление, что совместимость между двоичными файлами, созданными двумя разными компиляторами, возможна и / или безопасна, когда это не так. Поскольку C ++ не имеет стандартного ABI, стандартная схема искажения имен будет бессмысленной, потенциально вводящей в заблуждение и опасной.
Элквис
1
@Jim Раздел о флагах компилятора будет на порядок длиннее самого ответа. GCC компилируется с ним по умолчанию, поэтому "-fno-rtti", другие компиляторы могут не делать, но стандарт для флагов компилятора отсутствует.
kfsone
82
Очень некрасиво, но делает свое дело, если вам нужна только информация времени компиляции (например, для отладки):
auto testVar = std::make_tuple(1,1.0,"abc");decltype(testVar)::foo=1;
Возвращает:
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*>'
только c ++ может сделать это так сложно (печать типа авто-переменных во время компиляции). ТОЛЬКО С ++.
Карл Пикетт
3
@KarlP хорошо, чтобы быть справедливым, это немного запутанно, это тоже работает :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV
В VC ++ 17 это уменьшает ссылку на rvalue на обычную ссылку, даже в функции шаблона с параметром forwarding-reference и именем объекта, заключенным в std :: forward.
Джайв
Вы смогли добраться до типа без создания новых колес!
Стивен Экхофф
1
Эта методика также описана в «Пункте 4: Узнайте, как просматривать выведенные типы» в Effective Modern C ++
lenkite,
54
Не забудьте включить <typeinfo>
Я полагаю, что вы имеете в виду идентификацию типов во время выполнения. Вы можете достичь вышеуказанного, делая.
Таким образом, вы не можете использовать эту информацию для сериализации. Но все же, свойство typeid (a) .name () все еще может использоваться для целей журнала / отладки.
Не будет ли печататься «int» для шорт и чарс? И "плавать" на двоих?
gartenriese
1
Специализация @gartenriese лишена этого недостатка. Поскольку doubleэто скомпилирует неспециализированную версию функции шаблона, а не выполнит неявное преобразование типов, чтобы использовать специализацию: cpp.sh/2wzc
chappjc
1
@chappjc: Честно говоря, я не знаю, почему я тогда спрашивал об этом, теперь мне это ясно. Но все равно спасибо за ответ на годичный вопрос!
gartenriese
2
@gartenriese Я так и думал, но в какой-то момент у «интернета» может возникнуть тот же вопрос.
chappjc
18
Как уже упоминалось, typeid().name()может вернуть искаженное имя. В GCC (и некоторых других компиляторах) вы можете обойти это с помощью следующего кода:
#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;
DECLARE_TYPE_NAME существует, чтобы упростить вашу жизнь, объявляя этот класс черт для всех типов, которые вы ожидаете.
Это может быть более полезным, чем решения, связанные с этим, typeidпотому что вы можете контролировать вывод. Например, использование typeidfor long longв моем компиляторе дает "x".
В C ++ 11 у нас есть decltype. В стандарте c ++ нет способа отобразить точный тип переменной, объявленной с использованием decltype. Мы можем использовать boost typeindex ie type_id_with_cvr(cvr означает const, volatile, reference) для печати типа, как показано ниже.
#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;}
было бы проще использовать вспомогательную функцию:template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
r0ng
6
Вы также можете использовать c ++ Filter с опцией -t (type), чтобы разобрать имя типа:
Чёрт возьми, но сделаю то, что мне нужно. И намного меньше, чем другие решения. Работает на Mac, кстати.
Марко Луглио
6
Говард Хиннант использовал магические числа для извлечения имени типа. 瑋 桓 瑋 предложил строковый префикс и суффикс. Но префикс / суффикс постоянно меняется. С помощью «probe_type» type_name автоматически вычисляет размеры префикса и суффикса для «probe_type» для извлечения имени типа:
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 версия 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)
Другие ответы с участием RTTI (typeid), вероятно, то, что вы хотите, если:
Вы можете позволить себе перерасход памяти (который может быть значительным с некоторыми компиляторами)
имена классов, которые возвращает ваш компилятор, полезны
Альтернатива (аналогично ответу Грега Хьюгилла) заключается в построении таблицы характеристик во время компиляции.
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";};
Имейте в виду, что если вы оберните объявления в макрос, у вас будут проблемы с объявлением имен для типов шаблонов, принимающих более одного параметра (например, std :: map), из-за запятой.
Чтобы получить доступ к имени типа переменной, все, что вам нужно, это
(i) он не будет работать для других типов (то есть не является универсальным); (ii) бесполезный взлет кода; (iii) то же самое можно (правильно) сделать с помощью typeidили decltype.
edmz
2
Вы правы, но это охватывает все основные типы ... и это то, что мне нужно прямо сейчас ..
Джахид
2
Можете ли вы сказать мне, как бы вы сделали это с decltype,
Джахид
1
Если это тест во время компиляции, вы можете использовать std :: is_same <T, S> и decltype, чтобы получить T и S.
edmz
4
Когда я бросил вызов, я решил проверить, как далеко можно зайти с независящим от платформы (надеемся) обманом шаблона.
Имена собраны полностью во время компиляции. (Это средство typeid(T).name()нельзя использовать, поэтому вы должны явно указать имена для несоставных типов. В противном случае вместо них будут отображаться заполнители.)
Пример использования:
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)`.}
Код:
#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;}
Вывод:
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
Лучшее решение - позволить компилятору сгенерировать сообщение об ошибке во время вывода типа, например:
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;}
Результат будет примерно таким, в зависимости от компиляторов,
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;
Таким образом, мы получаем , чтобы узнать , что x«типа s является int, y» типа s являетсяconst int*
Для тех, кто все еще посещает, у меня недавно была та же проблема, и я решил написать небольшую библиотеку, основанную на ответах из этого поста. Он предоставляет имена типов constexpr и индексы типов и тестируется на Mac, Windows и Ubuntu.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Если вам нужна поддержка кросс-платформенной: Используйте#ifdef
,#else
,#endif
чтобы предоставить один макрос для других платформ , таких как MSVC.template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Затем использование egprint_T<const int * const **>();
будет печататьvoid print_T() [T = const int *const **]
во время выполнения и сохраняет все квалификаторы (работает в GCC и Clang).__PRETTY_FUNCTION__
это не стандарт C ++ (требование в названии вопроса).Ответы:
Обновление C ++ 11 до очень старого вопроса: тип переменной печати в C ++.
Принятым (и хорошим) ответом является использование
typeid(a).name()
, гдеa
имя переменной.Теперь в C ++ 11 мы имеем
decltype(x)
, который может превратить выражение в тип. Иdecltype()
поставляется со своим набором очень интересных правил. Например,decltype(a)
иdecltype((a))
обычно это будут разные типы (и по понятным и понятным причинам, когда эти причины будут выявлены).Поможет ли наш верный
typeid(a).name()
нам исследовать этот дивный новый мир?Нет.
Но инструмент, который будет не так сложен. И это тот инструмент, который я использую в качестве ответа на этот вопрос. Я буду сравнивать и сопоставлять этот новый инструмент
typeid(a).name()
. И этот новый инструмент на самом деле построен на вершинеtypeid(a).name()
.Основная проблема:
отбрасывает cv-квалификаторы, ссылки и lvalue / rvalue-ness. Например:
Для меня выводы:
и я предполагаю на выходах MSVC:
Т.е.
const
ушел. Это не проблема QOI (Качество реализации). Стандарт обязывает это поведение.Что я рекомендую ниже:
который будет использоваться следующим образом:
и для меня выводы:
<disclaimer>
Я не проверял это на MSVC.</disclaimer>
Но я приветствую отзывы тех, кто это делает.Решение C ++ 11
Я использую
__cxa_demangle
для платформ не MSVC, как рекомендует ipapadop в своем ответе на demangle типов. Но на MSVC яtypeid
доверяю разобрать имена (не проверено). И это ядро обернуто вокруг некоторого простого тестирования, которое обнаруживает, восстанавливает и сообщает cv-квалификаторы и ссылки на тип ввода.Результаты
С этим решением я могу сделать это:
и вывод:
Обратите внимание (например) на разницу между
decltype(i)
иdecltype((i))
. Прежний тип декларации оi
. Последний является «типом» выраженияi
. (выражения никогда не имеют ссылочного типа, но в качестве соглашенияdecltype
представляют выражения lvalue со ссылками lvalue).Таким образом, этот инструмент является отличным средством для изучения
decltype
, в дополнение к изучению и отладке собственного кода.Напротив, если бы я собирал это просто
typeid(a).name()
, без добавления потерянных cv-квалификаторов или ссылок, результат был бы:Т.е. все ссылки и cv-квалификаторы удаляются.
C ++ 14 Обновление
Когда вы думаете, что у вас есть решение проблемы, кто-то прибыл, кто-то всегда появляется из ниоткуда и показывает вам гораздо лучший способ. :-)
Этот ответ от Jamboree показывает, как получить имя типа в C ++ 14 во время компиляции. Это блестящее решение по нескольким причинам:
Ответ Джамбори не совсем раскрывает все для VS, и я немного подправил его код. Но так как этот ответ получает много просмотров, потратьте некоторое время, чтобы перейти к нему и высказать свой ответ, без которого это обновление никогда бы не произошло.
Этот код автоматически откатится,
constexpr
если вы все еще застряли в древнем C ++ 11. И если вы рисуете на стене пещеры с помощью C ++ 98/03,noexcept
это тоже принесет в жертву.C ++ 17 Обновление
В комментариях ниже Либерта указывает, что новое
std::string_view
может заменитьstatic_string
:Я обновил константы для VS благодаря очень хорошей детективной работе Джайва Дадсона в комментариях ниже.
Обновить:
Обязательно ознакомьтесь с этой перепиской ниже, которая устраняет нечитаемые магические числа в моей последней формулировке.
источник
#include <iostream>
строку.declval
можно сделать).std::string_view
вместоstatic_string
?Пытаться:
Возможно, вам придется активировать RTTI в опциях компилятора, чтобы это работало. Кроме того, вывод этого зависит от компилятора. Это может быть необработанное имя типа или символ искажения имени или что-то среднее между ними.
источник
Очень некрасиво, но делает свое дело, если вам нужна только информация времени компиляции (например, для отладки):
Возвращает:
источник
auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
Не забудьте включить
<typeinfo>
Я полагаю, что вы имеете в виду идентификацию типов во время выполнения. Вы можете достичь вышеуказанного, делая.
источник
Согласно решению Говарда , если вы не хотите магическое число, я думаю, что это хороший способ представить и выглядит интуитивно понятным:
источник
Обратите внимание, что имена, созданные функцией RTTI в C ++, не являются переносимыми. Например, класс
будет иметь следующие имена:
Таким образом, вы не можете использовать эту информацию для сериализации. Но все же, свойство typeid (a) .name () все еще может использоваться для целей журнала / отладки.
источник
Вы можете использовать шаблоны.
В приведенном выше примере, когда тип не соответствует, он выведет «unknown».
источник
double
это скомпилирует неспециализированную версию функции шаблона, а не выполнит неявное преобразование типов, чтобы использовать специализацию: cpp.sh/2wzcКак уже упоминалось,
typeid().name()
может вернуть искаженное имя. В GCC (и некоторых других компиляторах) вы можете обойти это с помощью следующего кода:}
источник
Вы можете использовать класс черт для этого. Что-то вроде:
DECLARE_TYPE_NAME
существует, чтобы упростить вашу жизнь, объявляя этот класс черт для всех типов, которые вы ожидаете.Это может быть более полезным, чем решения, связанные с этим,
typeid
потому что вы можете контролировать вывод. Например, использованиеtypeid
forlong long
в моем компиляторе дает "x".источник
В C ++ 11 у нас есть decltype. В стандарте c ++ нет способа отобразить точный тип переменной, объявленной с использованием decltype. Мы можем использовать boost typeindex ie
type_id_with_cvr
(cvr означает const, volatile, reference) для печати типа, как показано ниже.источник
template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
Вы также можете использовать c ++ Filter с опцией -t (type), чтобы разобрать имя типа:
Проверено только на Linux.
источник
Говард Хиннант использовал магические числа для извлечения имени типа. 瑋 桓 瑋 предложил строковый префикс и суффикс. Но префикс / суффикс постоянно меняется. С помощью «probe_type» type_name автоматически вычисляет размеры префикса и суффикса для «probe_type» для извлечения имени типа:
Вывод
gcc 10.0.0 20190919 Wandbox:
Clang 10.0.0 Wandbox:
VS 2019 версия 16.3.3:
источник
Другие ответы с участием RTTI (typeid), вероятно, то, что вы хотите, если:
Альтернатива (аналогично ответу Грега Хьюгилла) заключается в построении таблицы характеристик во время компиляции.
Имейте в виду, что если вы оберните объявления в макрос, у вас будут проблемы с объявлением имен для типов шаблонов, принимающих более одного параметра (например, std :: map), из-за запятой.
Чтобы получить доступ к имени типа переменной, все, что вам нужно, это
источник
Более общее решение без перегрузки функций, чем в моем предыдущем:
Здесь MyClass - это определенный пользователем класс. Здесь также можно добавить больше условий.
Пример:
Вывод:
источник
Мне нравится метод Ника. Полная форма может быть такой (для всех основных типов данных):
источник
typeid
илиdecltype
.Когда я бросил вызов, я решил проверить, как далеко можно зайти с независящим от платформы (надеемся) обманом шаблона.
Имена собраны полностью во время компиляции. (Это средство
typeid(T).name()
нельзя использовать, поэтому вы должны явно указать имена для несоставных типов. В противном случае вместо них будут отображаться заполнители.)Пример использования:
Код:
источник
Вывод:
источник
Как объяснил Скотт Мейерс в Effective Modern C ++,
Лучшее решение - позволить компилятору сгенерировать сообщение об ошибке во время вывода типа, например:
Результат будет примерно таким, в зависимости от компиляторов,
Таким образом, мы получаем , чтобы узнать , что
x
«типа s являетсяint
,y
» типа s являетсяconst int*
источник
Для тех, кто все еще посещает, у меня недавно была та же проблема, и я решил написать небольшую библиотеку, основанную на ответах из этого поста. Он предоставляет имена типов constexpr и индексы типов и тестируется на Mac, Windows и Ubuntu.
Код библиотеки находится здесь: https://github.com/TheLartians/StaticTypeInfo
источник