можно ли напечатать тип переменной в стандартном c ++?

<p>например:</p><pre><code>int a = 12; cout << typeof(a) << endl; </code></pre><p>ожидаемый результат:</p><pre><code>int </code></pre>

<p>обновление c++ 11 до очень старого вопроса: тип переменной печати в c++.</p><p>принятым (и хорошим) ответом является использование <code>typeid(a).name()</code>, где <code>a</code> - имя переменной.</p><p>теперь в c++ 11 у нас есть <code>decltype(x)</code>, которая может превратить выражение в тип. и <code>decltype()</code> поставляется с собственным набором очень интересных правил. например, <code>decltype(a)</code> и <code>decltype((a))</code>, как правило, будут разных типов (и по понятным и понятным причинам, когда эти причины будут выявлены).</p><p>поможет ли наш верный <code>typeid(a).name()</code> исследовать этот дивный новый мир?</p><p>нет.</p><p>но инструмент, который будет не так сложен. и это тот инструмент, который я использую в качестве ответа на этот вопрос. я буду сравнивать и сравнивать этот новый инструмент с <code>typeid(a).name()</code>. и этот новый инструмент фактически построен поверх <code>typeid(a).name()</code>.</p><p><strong> основная проблема: </strong></p><pre><code>typeid(a).name() </code></pre><p>отбрасывает cv-квалификаторы, ссылки и lvalue/rvalue-ness. например:</p><pre><code>const int ci = 0; std::cout << typeid(ci).name() << '\n'; </code></pre><p>для меня выводы:</p><pre><code>i </code></pre><p>и я предполагаю на выходах msvc:</p><pre><code>int </code></pre><p>то есть <code>const</code> ушел. это не проблема qoi (качество реализации). стандарт обязывает это поведение.</p><p>что я рекомендую ниже:</p><pre><code>template <typename t> std::string type\_name(); </code></pre><p>который будет использоваться следующим образом:</p><pre><code>const int ci = 0; std::cout << type\_name<decltype(ci)>() << '\n'; </code></pre><p>и для меня выводы:</p><pre><code>int const </code></pre><p><code><disclaimer></code> я не проверял это на msvc. <code></disclaimer></code> но я приветствую отзывы тех, кто это делает.</p><p><strong> решение c++ 11 </strong></p><p>я использую <code>\_\_cxa\_demangle</code> для не-msvc-платформ, как рекомендовано <a href="https://stackoverflow.com/users/487362/ipapadop">ipapadop</a> в его ответе на типы demangle. но на msvc я доверяю <code>typeid</code>, чтобы разобрать имена (не проверено). и это ядро обернуто вокруг некоторого простого тестирования, которое обнаруживает, восстанавливает и сообщает cv-квалификаторы и ссылки на тип ввода.</p><pre><code>#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; } </code></pre><p><strong> результаты </strong></p><p>с этим решением я могу сделать это:</p><pre><code>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'; } </code></pre><p>и вывод:</p><pre><code>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 </code></pre><p>обратите внимание (например) на разницу между <code>decltype(i)</code> и <code>decltype((i))</code>. первым является тип <em> объявления </em> <code>i</code>. последний является "типом" выражения <em> </em> <code>i</code>. (выражения никогда не имеют ссылочного типа, но в качестве соглашения <code>decltype</code> представляет выражения lvalue с ссылками lvalue).</p><p>таким образом, этот инструмент является отличным средством для изучения <code>decltype</code>, в дополнение к изучению и отладке собственного кода.</p><p>напротив, если бы я построил это только на <code>typeid(a).name()</code>, без добавления обратно потерянных cv-квалификаторов или ссылок, результат был бы:</p><pre><code>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 </code></pre><p>то есть все ссылки и cv-квалификатор удаляются.</p><p><strong> обновление c++ 14 </strong></p><p>когда вы думаете, что у вас есть решение проблемы, кто-то прибыл, кто-то всегда появляется из ниоткуда и показывает вам гораздо лучший способ. :-)</p><p> <a href="https://stackoverflow.com/a/35943472/576911">этот ответ</a> from <a href="https://stackoverflow.com/users/2969631/jamboree">jamboree</a> показывает, как получить имя типа в c++ 14 во время компиляции. это блестящее решение по нескольким причинам:</p><ol> <li>это во время компиляции!</li> <li>вы получаете сам компилятор для выполнения работы вместо библиотеки (даже std :: lib). это означает более точные результаты для новейших языковых функций (таких как лямбды).</li> </ol><p> <a href="https://stackoverflow.com/users/2969631/jamboree">джамбори</a> <a href="https://stackoverflow.com/a/35943472/576911">ответ</a> не совсем все выкладывает для vs, и я немного подправил его код. но так как этот ответ получает много просмотров, потратьте некоторое время, чтобы перейти к нему и высказать свой ответ, без которого это обновление никогда бы не произошло.</p><pre><code>#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 } </code></pre><p>этот код автоматически откатится на <code>constexpr</code>, если вы все еще застряли в древнем c++ 11. и если вы рисуете на стене пещеры с помощью c++ 98/03, <code>noexcept</code> также приносится в жертву.</p><p><strong> обновление c++ 17 </strong></p><p>в комментариях ниже <a href="https://stackoverflow.com/users/3624760/lyberta">lyberta</a> указывает, что новый <code>std::string\_view</code> может заменить <code>static\_string</code>:</p><pre><code>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 } </code></pre><p>я обновил константы для vs благодаря очень хорошей детективной работе джайва дадсона в комментариях ниже.</p>

<p>пытаться:</p><pre class="lang-cpp prettyprint-override"><code>#include <typeinfo> // … std::cout << typeid(a).name() << '\n'; </code></pre><p>возможно, вам придется активировать rtti в опциях компилятора, чтобы это работало. кроме того, вывод этого зависит от компилятора. это может быть необработанное имя типа или символ искажения имени или что-то среднее между ними.</p>

<p>очень некрасиво, но делает свое дело, если вам нужна только информация времени компиляции (например, для отладки):</p><pre><code>auto testvar = std::make\_tuple(1, 1.0, "abc"); decltype(testvar)::foo = 1; </code></pre><p>возвращает:</p><pre><code>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*>' </code></pre>

<p>не забудьте включить <code><typeinfo></code></p><p>я полагаю, что вы имеете в виду идентификацию типов во время выполнения. вы можете достичь вышеуказанного, делая.</p><pre><code>#include <iostream> #include <typeinfo> using namespace std; int main() { int i; cout << typeid(i).name(); return 0; } </code></pre>

<p>обратите внимание, что имена, созданные функцией rtti в c++, <strong>not</strong> portable. например, класс</p><pre><code>mynamespace::cmycontainer<int, test\_mynamespace::cmyobject> </code></pre><p>будет иметь следующие имена:</p><pre><code>// msvc 2003: class mynamespace::cmycontainer[int,class test\_mynamespace::cmyobject] // g++ 4.2: n8mynamespace8cmycontaineriin13test\_mynamespace9cmyobjecteee </code></pre><p>таким образом, вы не можете использовать эту информацию для сериализации. но все же, свойство typeid (a) .name () все еще может использоваться для целей журнала/отладки.</p>

<p>вы можете использовать шаблоны.</p><pre><code>template <typename t> const char* typeof(t&) { return "unknown"; } // default template<> const char* typeof(int&) { return "int"; } template<> const char* typeof(float&) { return "float"; } </code></pre><p>в приведенном выше примере, когда тип не соответствует, он выведет "unknown".</p>

<p>как уже упоминалось, <code>typeid().name()</code> может возвращать искаженное имя. в gcc (и некоторых других компиляторах) вы можете обойти это с помощью следующего кода:</p><pre><code>#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; </code></pre><p>}</p>

<p>вы можете использовать класс черт для этого. что-то вроде:</p><pre><code>#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; } </code></pre><p>определение <code>declare\_type\_name</code> существует, чтобы упростить вашу жизнь, объявляя этот класс черт для всех типов, которые вы ожидаете получить.</p><p>это может быть более полезным, чем решения, использующие <code>typeid</code>, потому что вы можете контролировать вывод. например, использование <code>typeid</code> для <code>long long</code> в моем компиляторе дает "x".</p>

<p>в c++ 11 у нас есть decltype. в стандарте c ++ нет способа отобразить точный тип переменной, объявленной с использованием decltype. мы можем использовать boost typeindex, т.е. <code>type\_id\_with\_cvr</code> (cvr означает const, volatile, reference) для печати типа, как показано ниже.</p><pre><code>#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; } </code></pre>

<p>другие ответы с участием rtti (typeid), вероятно, то, что вы хотите, если:</p><ul> <li>вы можете позволить себе перерасход памяти (который может быть значительным с некоторыми компиляторами)</li> <li>имена классов, которые возвращает ваш компилятор, полезны</li> </ul><p>альтернатива (аналогично ответу грега хьюгилла) заключается в построении таблицы характеристик во время компиляции.</p><pre><code>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"; }; </code></pre><p>имейте в виду, что если вы оберните объявления в макрос, у вас будут проблемы с объявлением имен для типов шаблонов, принимающих более одного параметра (например, std :: map), из-за запятой.</p><p>чтобы получить доступ к имени типа переменной, все, что вам нужно, это</p><pre><code>template <typename t> const char* get\_type\_as\_string(const t&) { return type\_as\_string<t>::value; } </code></pre>

<p>вы также можете использовать c ++ filter с опцией -t (type), чтобы разобрать имя типа:</p><pre><code>#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; } </code></pre><p>проверено только на linux.</p>

<p>более общее решение без перегрузки функций, чем в моем предыдущем:</p><pre><code>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;} </code></pre><p>здесь myclass - это определенный пользователем класс. здесь также можно добавить больше условий.</p><p>пример:</p><pre><code>#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;} </code></pre><p>результат:</p><pre><code>int string myclass </code></pre>

<p>мне нравится метод ника, полная форма может быть такой (для всех основных типов данных):</p><pre><code>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*"; } </code></pre>

<p>когда я бросил вызов, я решил проверить, как далеко можно зайти с независящим от платформы (надеемся) обманом шаблона.</p><p>имена собраны полностью во время компиляции. (это означает, что <code>typeid(t).name()</code> не может быть использован, поэтому вы должны явно указать имена для не составных типов. в противном случае вместо них будут отображаться заполнители.)</p><p>пример использования:</p><pre><code>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)`. } </code></pre><p>код:</p><pre><code>#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 '\0'; } constexpr std::size\_t sl\_len(const char *str) { for (std::size\_t i = 0; i < max\_str\_lit\_len; i++) if (str[i] == '\0') return i; return 0; } template <char ...c> struct str\_lit { static constexpr char value[] {c..., '\0'}; 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);}; </code></pre>

<pre><code>#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; } </code></pre><p>результат:</p><pre><code>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 </code></pre>

<p>как объяснил скотт мейерс в effective modern c++,</p><blockquote> <p>звонки на <code>std::type\_info::name</code> не гарантированно возвращают что-либо разумное.</p> </blockquote><p>лучшее решение - позволить компилятору генерировать сообщение об ошибке во время вывода типа, например:</p><pre><code>template<typename t> class td; int main(){ const int theanswer = 32; auto x = theanswer; auto y = &theanswer; td<decltype(x)> xtype; td<decltype(y)> ytype; return 0; } </code></pre><p>результат будет примерно таким, в зависимости от компиляторов,</p><pre><code>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<const int *> ytype’ has incomplete type and cannot be defined td<decltype(y)> ytype; </code></pre><p>следовательно, мы узнаем, что тип <code>x</code> равен <code>int</code>, тип <code>y</code> равен <code>const int*</code></p>