Можно ли вывести имя типа переменной в стандартном C++?
Проблема typeid().name() и её решение
Действительно, typeid().name() возвращает "искажённые" (mangled) имена типов. Это внутреннее представление компилятора, которое не предназначено для чтения человеком. В разных компиляторах эти имена выглядят по-разному: MSVC может показать "int", а GCC/Clang — "i". К счастью, есть несколько способов получить нормальные имена.
Способ 1: Использование макроса и шаблона (рекомендуется)
Лучший кроссплатформенный метод — написать простую функцию-шаблон, которая использует __PRETTY_FUNCTION__ (GCC/Clang) или __FUNCSIG__ (MSVC). Эти макросы содержат полную сигнатуру функции, включая имя типа.
#include <iostream>
#include <string>
#include <type_traits>
// Функция для получения имени типа в читаемом виде
template <typename T>
constexpr std::string_view type_name() {
#if defined(__clang__) || defined(__GNUC__)
// Для GCC и Clang
return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
// Для MSVC
return __FUNCSIG__;
#endif
}
// Утилита для извлечения чистого имени
template <typename T>
std::string type_name_clean() {
std::string_view raw = type_name<T>();
// Ищем позицию имени типа в строке сигнатуры
auto start = raw.find('=');
if (start == std::string_view::npos) start = 0;
else start += 2; // пропускаем "= "
auto end = raw.find(';', start);
if (end == std::string_view::npos) end = raw.find(']', start);
if (end == std::string_view::npos) end = raw.size() - 1;
return std::string(raw.substr(start, end - start));
}
int main() {
int a = 5;
const char* str = "hello";
auto x = a + 3.14;
std::cout << type_name_clean<decltype(a)>() << std::endl; // "int"
std::cout << type_name_clean<decltype(str)>() << std::endl; // "const char*"
std::cout << type_name_clean<decltype(x)>() << std::endl; // "double"
return 0;
}
Способ 2: Использование Boost.TypeIndex
Если вы используете Boost, есть готовое решение:
#include <boost/type_index.hpp>
#include <iostream>
int main() {
int a = 5;
auto b = a + 3.14f;
std::cout << boost::typeindex::type_id<decltype(a)>().pretty_name() << std::endl; // "int"
std::cout << boost::typeindex::type_id<decltype(b)>().pretty_name() << std::endl; // "float"
return 0;
}
Как работает первый способ?
- __PRETTY_FUNCTION__ — в GCC/Clang содержит полную сигнатуру функции, например:
constexpr std::string_view type_name() [with T = int; ...] - __FUNCSIG__ — в MSVC делает то же самое:
class std::basic_string_view<char,struct std::char_traits<char> > __cdecl type_name<int>(void) - Мы извлекаем имя типа, находя часть после "T = " (или аналогичного разделителя).
Важно!
В C++23 появился оператор std::is_same_v и улучшенный std::source_location, но для получения строкового представления типа всё ещё нет стандартного способа. Начиная с C++26, возможно, добавится std::type_info::name() с читаемыми именами, но пока приходится использовать трюки с макросами.
Для простого отладочного вывода можно использовать более короткую версию:
#define TYPE_NAME(x) []() { \
std::cout << #x << " is " << type_name_clean<decltype(x)>() << std::endl; \
}()
int main() {
int a = 5;
TYPE_NAME(a); // выведет: "a is int"
}
<<Альтернативный подход: ручное декодирование typeid().name()
Если вы не хотите использовать макросы или Boost, можно написать функцию для декодирования "искажённых" имён. Это работает только для GCC/Clang, так как MSVC использует другую схему:
#include <iostream>
#include <typeinfo>
#include <cxxabi.h> // только для GCC/Clang
std::string demangle(const char* mangled_name) {
int status = 0;
char* demangled = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
std::string result = (status == 0) ? demangled : mangled_name;
std::free(demangled);
return result;
}
int main() {
auto x = 42.5f;
std::cout << demangle(typeid(x).name()) << std::endl; // "float"
return 0;
}
Недостатки этого метода
- Работает только на GCC/Clang (требуется
cxxabi.h). - Не подходит для MSVC (там нужно использовать
UnDecorateSymbolNameиз Windows API). - Медленнее, чем использование макросов, так как требует динамической аллокации.
Для простых проектов этот способ сойдёт, но для серьёзной разработки лучше использовать первый вариант из основного ответа.