Можно ли напечатать тип переменной в стандартном C++?
например:
int a = 12;
cout << typeof(a) << endl;
ожидаемый результат:
int
15 ответов:
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. Например:
const int ci = 0; std::cout << typeid(ci).name() << '\n';
для меня выходы:
i
и я предполагаю, что на выходах MSVC:
int
т. е. элемент
const
ушел. Это не вопрос QOI (качества реализации). Стандарт предписывает такое поведение.то, что я рекомендую ниже:
template <typename T> std::string type_name();
который будет использоваться следующим образом:
const int ci = 0; std::cout << type_name<decltype(ci)>() << '\n';
а для меня выходы:
int const
<disclaimer>
у меня есть не проверял это на MSVC.</disclaimer>
но я приветствую обратную связь от тех, кто.Решение C++11
я использую
__cxa_demangle
для платформ, отличных от MSVC, как рекомендует ipapadop в своем ответе на типы demangle. Но на MSVC я доверяюtypeid
в demangle имена (непроверено). И это ядро обернуто вокруг некоторого простого тестирования, которое обнаруживает, восстанавливает и сообщает 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() { typedef typename 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), #else nullptr, #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 += "&"; else if (std::is_rvalue_reference<T>::value) r += "&&"; return r; }
Результаты
С помощью этого решения я могу сделать следующее:
int& foo_lref(); int&& foo_rref(); int foo_value(); int main() { int i = 0; const int 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 int decltype((i)) is int& decltype(ci) is int const decltype((ci)) is int const& decltype(static_cast<int&>(i)) is int& decltype(static_cast<int&&>(i)) is int&& decltype(static_cast<int>(i)) is int decltype(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 int decltype((i)) is int decltype(ci) is int decltype((ci)) is int decltype(static_cast<int&>(i)) is int decltype(static_cast<int&&>(i)) is int decltype(static_cast<int>(i)) is int decltype(foo_lref()) is int decltype(foo_rref()) is int decltype(foo_value()) is int
т. е. каждая ссылка и cv-квалификатор снимается.
Обновление C++14
просто, когда вы думаю, что у вас есть решение проблемы прибил, кто-то всегда приходит из ниоткуда и показывает вам гораздо лучший способ. : -)
ответ с Jamboree показано, как получить имя типа в C++14 во время компиляции. Это блестящее решение по нескольким причинам:
- это во время компиляции!
- вы получаете сам компилятор, чтобы сделать работу вместо библиотеки (даже std::lib). Это значит более точное результаты для последних языковых функций (например, лямбды).
Джембори!--76-->ответ не совсем выложил все для VS, и я немного подправляю его код. Но поскольку этот ответ получает много просмотров, потратьте некоторое время, чтобы перейти туда и озвучить свой ответ, без которого это обновление никогда бы не произошло.
#include <cstddef> #include <stdexcept> #include <cstring> #include <ostream> #ifndef _MSC_VER # if __cplusplus < 201103 # define CONSTEXPR11_TN # define CONSTEXPR14_TN # define NOEXCEPT_TN # elif __cplusplus < 201402 # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN # define NOEXCEPT_TN noexcept # else # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN constexpr # define NOEXCEPT_TN noexcept # endif #else // _MSC_VER # if _MSC_VER < 1900 # define CONSTEXPR11_TN # define CONSTEXPR14_TN # define NOEXCEPT_TN # elif _MSC_VER < 2000 # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN # define NOEXCEPT_TN noexcept # else # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN constexpr # define NOEXCEPT_TN noexcept # endif #endif // _MSC_VER class static_string { const char* const p_; const std::size_t sz_; public: typedef const char* const_iterator; template <std::size_t N> CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN : p_(a) , sz_(N-1) {} CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN : p_(p) , sz_(N) {} CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;} CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;} CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;} CONSTEXPR11_TN const_iterator end() const NOEXCEPT_TN {return p_ + sz_;} CONSTEXPR11_TN char operator[](std::size_t n) const { return n < sz_ ? p_[n] : throw std::out_of_range("static_string"); } }; inline std::ostream& operator<<(std::ostream& os, static_string const& s) { return os.write(s.data(), s.size()); } template <class T> CONSTEXPR14_TN static_string type_name() { #ifdef __clang__ static_string p = __PRETTY_FUNCTION__; return static_string(p.data() + 31, p.size() - 31 - 1); #elif defined(__GNUC__) static_string p = __PRETTY_FUNCTION__; # if __cplusplus < 201402 return static_string(p.data() + 36, p.size() - 36 - 1); # else return static_string(p.data() + 46, p.size() - 46 - 1); # endif #elif defined(_MSC_VER) static_string p = __FUNCSIG__; return static_string(p.data() + 38, p.size() - 38 - 7); #endif }
этот код будет автоматически откат на
constexpr
если вы все еще застряли в Древнем C++11. И если вы рисуете на стене пещеры с C++98/03, тоnoexcept
также приносится в жертву.Обновление C++17
в комментариях ниже. Lyberta указывает, что новый
std::string_view
заменитьstatic_string
:template <class T> constexpr std::string_view type_name() { using namespace 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 < 201402 return string_view(p.data() + 36, p.size() - 36 - 1); # else return 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 благодаря очень хорошей детективной работе джайва Дадсона в комментариях ниже.
попробуй:
#include <typeinfo> // … std::cout << typeid(a).name() << '\n';
возможно, вам придется активировать RTTI в настройках компилятора, чтобы это работало. Кроме того, выход этого зависит от компилятора. Это может быть имя необработанного типа или имя, искажающее символ или что-то между ними.
очень некрасиво, но делает трюк, если вы хотите только информацию о времени компиляции (например, для отладки):
auto testVar = std::make_tuple(1, 1.0, "abc"); static_assert(decltype(testVar)::dummy_error, "DUMP MY TYPE" );
возвращает:
Compilation finished with errors: source.cpp: In function 'int main()': source.cpp:5:19: error: 'dummy_error' is not a member of 'std::tuple<int, double, const char*>'
Не забудьте включить
<typeinfo>
Я считаю, что вы имеете в виду идентификацию типа времени выполнения. Вы можете достичь вышеуказанного, делая .
#include <iostream> #include <typeinfo> using namespace std; int main() { int i; cout << typeid(i).name(); return 0; }
обратите внимание, что имена созданные с помощью функции библиотеку RTTI в C++ - это не портативное. Например, класс
MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>
будет иметь следующие названия:
// MSVC 2003: class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject] // G++ 4.2: N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE
поэтому вы не можете использовать эту информацию для сериализации. Но тем не менее, свойство typeid(a).name() все еще может использоваться для целей журнала/отладки
вы можете использовать шаблоны.
template <typename T> const char* typeof(T&) { return "unknown"; } // default template<> const char* typeof(int&) { return "int"; } template<> const char* typeof(float&) { return "float"; }
в приведенном выше примере, когда тип не соответствует он будет печатать "неизвестно".
Как уже упоминалось,
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; // unmangled int 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); return 0;
}
вы можете использовать класс признаков для этого. Что-то вроде:
#include <iostream> using namespace std; template <typename T> class type_name { public: static const char *name; }; #define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x; #define GET_TYPE_NAME(x) (type_name<typeof(x)>::name) DECLARE_TYPE_NAME(int); int main() { int a = 12; cout << GET_TYPE_NAME(a) << endl; }
The
DECLARE_TYPE_NAME
define существует, чтобы сделать вашу жизнь проще в объявлении этого класса признаков для всех типов, которые вам понадобятся.Это может быть более полезным, чем решения, с участием
typeid
потому что вы можете контролировать выход. Например, с помощьюtypeid
наlong long
на моем компиляторе дает "x".
другие ответы, связанные с RTTI (typeid), вероятно, то, что вы хотите, если:
- вы можете позволить себе накладные расходы памяти (которые могут быть значительными с некоторыми компиляторами)
- имена классов, возвращаемые компилятором, полезны
альтернатива, (похож на ответ Грег Hewgill х), построить компиляции таблицы признаков.
template <typename T> struct type_as_string; // declare your Wibble type (probably with definition of Wibble) template <> struct type_as_string<Wibble> { static const char* const value = "Wibble"; };
имейте в виду, что если вы обернете объявления в макрос, у вас будут проблемы объявление имен для типов шаблонов, принимающих более одного параметра (например, std::map), из-за запятой.
чтобы получить имя типа переменной, все, что вам нужно-это
template <typename T> const char* get_type_as_string(const T&) { return type_as_string<T>::value; }
В C++11, у нас есть decltype. В стандартном c++ нет способа отобразить точный тип переменной, объявленной с помощью decltype. Мы можем использовать boost typeindex т. е.
type_id_with_cvr
(cvr означает const, volatile, reference) для печати типа, как показано ниже.#include <iostream> #include <boost/type_index.hpp> using namespace std; using boost::typeindex::type_id_with_cvr; int main() { int i = 0; const int 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'; return 0; }
более общее решение без перегрузки функций, чем мое предыдущее:
template<typename T> std::string TypeOf(T){ std::string Type="unknown"; if(std::is_same<T,int>::value) Type="int"; if(std::is_same<T,std::string>::value) Type="String"; if(std::is_same<T,MyClass>::value) Type="MyClass"; return Type;}
здесь MyClass-это определенный пользователем класс. Здесь также можно добавить дополнительные условия.
пример:
#include <iostream> class MyClass{}; template<typename T> std::string TypeOf(T){ std::string Type="unknown"; if(std::is_same<T,int>::value) Type="int"; if(std::is_same<T,std::string>::value) Type="String"; if(std::is_same<T,MyClass>::value) Type="MyClass"; return Type;} int main(){; int a=0; std::string s=""; MyClass my; std::cout<<TypeOf(a)<<std::endl; std::cout<<TypeOf(s)<<std::endl; std::cout<<TypeOf(my)<<std::endl; return 0;}
выход:
int String MyClass
Мне нравится метод Ника, полная форма может быть такой (для всех основных типов данных):
template <typename T> const char* typeof(T&) { return "unknown"; } // default template<> const char* typeof(int&) { return "int"; } template<> const char* typeof(short&) { return "short"; } template<> const char* typeof(long&) { return "long"; } template<> const char* typeof(unsigned&) { return "unsigned"; } template<> const char* typeof(unsigned short&) { return "unsigned short"; } template<> const char* typeof(unsigned long&) { return "unsigned long"; } template<> const char* typeof(float&) { return "float"; } template<> const char* typeof(double&) { return "double"; } template<> const char* typeof(long double&) { return "long double"; } template<> const char* typeof(std::string&) { return "String"; } template<> const char* typeof(char&) { return "char"; } template<> const char* typeof(signed char&) { return "signed char"; } template<> const char* typeof(unsigned char&) { return "unsigned char"; } template<> const char* typeof(char*&) { return "char*"; } template<> const char* typeof(signed char*&) { return "signed char*"; } template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }
вы также можете использовать C++Филт с опцией-T (тип) в demangle имя типа:
#include <iostream> #include <typeinfo> #include <string> using namespace std; int main() { auto x = 1; string my_type = typeid(x).name(); system(("echo " + my_type + " | c++filt -t").c_str()); return 0; }
тестируется только на linux.
поскольку я бросаю вызов, я решил проверить, как далеко можно зайти с независимым от платформы (надеюсь) шаблонным обманом.
имена собираются полностью во время компиляции. (Что означает
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],const int, void (*)(std::string)))(volatile int*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> static constexpr std::size_t max_str_lit_len = 256; template <std::size_t I, std::size_t N> constexpr char sl_at(const char (&str)[N]) { if constexpr(I < N) return str[I]; else return ''; } constexpr std::size_t sl_len(const char *str) { for (std::size_t i = 0; i < max_str_lit_len; i++) if (str[i] == '') return i; return 0; } template <char ...C> struct str_lit { static constexpr char value[] {C..., ''}; static constexpr int 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, const char *> struct trim_str_lit_impl; template <std::size_t ...I, const char *S> struct trim_str_lit_impl<std::index_sequence<I...>, S> { using type = str_lit<S[I]...>; }; template <std::size_t N, const char *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> constexpr auto make_str_lit(const char (&str)[N]) { return trim_str_lit<sl_len((const char (&)[N])str), str>{}; } template <std::size_t A, std::size_t B> struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow<A,B-1>::value;}; template <std::size_t A> struct cexpr_pow<A,0> {static constexpr 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...>> { static constexpr auto func() { if constexpr (N >= cexpr_pow<10,X>::value) return num_to_str_lit_impl<N, X+1>::func(); else return 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> inline constexpr bool ptr_or_ref = std::is_pointer_v<T> || std::is_reference_v<T> || std::is_member_pointer_v<T>; template <typename T> inline constexpr bool 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> inline constexpr const char *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<const 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<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> using namespace 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); return 0; }
выход:
Type of a: St16initializer_listIPKcE Real type of a: std::initializer_list<char const*> Type of s: PKc Real type of s: char const* one two three Type of i: i Real type of i: int