Например:
int a = 12;
cout << typeof(a) << endl;
Ожидаемый результат:
int
Например:
int a = 12;
cout << typeof(a) << endl;
Ожидаемый результат:
int
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 в своем ответе на разделение типов. Но в 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()
{
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 Обновление
Когда вы думаете, что у вас есть решение проблемы, кто-то прибыл, кто-то всегда появляется из ниоткуда и показывает вам гораздо лучший способ. :-)
Этот ответ из Джамбори показывает, как получить имя типа в C++ 14 во время компиляции. Это блестящее решение по нескольким причинам:
Джамбори answer не совсем выкладывает все для 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 Обновление
В комментариях ниже Либерта указывает, что новый 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 благодаря очень хорошей детективной работе Джайва Дадсона в комментариях ниже.
Обязательно ознакомьтесь с этой перепиской ниже, которая устраняет нечитаемые магические числа в моей последней формулировке.
Try:
#include <typeinfo>
// …
std::cout << typeid(a).name() << '\n';
Возможно, вам придется активировать RTTI в настройках вашего компилятора, чтобы это работало. Кроме того, вывод этого зависит от компилятора. Это может быть имя сырого типа или символ переключения имени или что-то среднее между ними.
Очень некрасиво, но делает свое дело, если вам нужна только информация времени компиляции (например, для отладки):
auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo= 1;
Returns:
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*>'
Не забудьте включить <typeinfo>
Я считаю, что вы имеете в виду идентификацию типа времени выполнения. Вы можете сделать это выше.
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
int i;
cout << typeid(i).name();
return 0;
}
Обратите внимание, что имена, сгенерированные функцией RTTI на С++, являются не переносимыми. Например, класс
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"; }
В приведенном выше примере, когда тип не сопоставляется, он будет печатать "unknown".
Как уже упоминалось, 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;
}
В соответствии с решением Говарда, если вам не нужен магический номер, я думаю, что это хороший способ представить и выглядит интуитивно понятным:
template <typename T>
constexpr auto type_name()
{
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
Для этого вы можете использовать класс признаков. Что-то вроде:
#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;
}
Определяет DECLARE_TYPE_NAME
define, чтобы упростить вашу жизнь при объявлении этого класса признаков для всех типов, которые вы ожидаете.
Это может быть более полезно, чем решения, связанные с typeid
, потому что вы можете контролировать вывод. Например, использование typeid
для long long
в моем компиляторе дает "x".
В С++ 11 у нас есть decltype. В стандартном С++ нет способа отображения точного типа переменной, объявленной с помощью decltype. Мы можем использовать boost typeindex i.e 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;
}
Другие ответы, связанные с RTTI (typeid), вероятно, вы хотите, если:
Альтернатива (аналогично ответу Грега Хьюглилла) заключается в построении таблицы характеристик времени компиляции.
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;
}
Более общее решение без перегрузки функций, чем предыдущее:
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
Мне нравится метод Nick, полная форма может быть такой (для всех основных типов данных):
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*"; }
Вы также можете использовать С++ filter с опцией -t (type), чтобы развернуть имя типа:
#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 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 '\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);};
#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
Говард Хиннант использовал магические числа, чтобы извлечь имя типа. 康 桓 瑋 предложил префикс строки и суффикс. Но префикс/суффикс постоянно меняется. С помощью 'probe_type' type_name автоматически вычисляет размеры префикса и суффикса для 'probe_type' для извлечения имени типа:
#include <iostream>
#include <string_view>
using namespace std;
class probe_type;
template <typename T>
constexpr string_view type_name() {
string_view probe_type_name("class probe_type");
const string_view class_specifier("class");
string_view name;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
probe_type_name.remove_prefix(class_specifier.length());
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
probe_type_name.remove_prefix(class_specifier.length());
#elif defined(_MSC_VER)
name = __FUNCSIG__;
#endif
if (name.find(probe_type_name) != string_view::npos)
return name;
const string_view probe_type_raw_name = type_name<probe_type>();
const size_t prefix_size = probe_type_raw_name.find(probe_type_name);
name.remove_prefix(prefix_size);
name.remove_suffix(probe_type_raw_name.length() - prefix_size - probe_type_name.length());
return name;
}
class test;
int main() {
cout << type_name<test>() << endl;
cout << type_name<const int*&>() << endl;
cout << type_name<unsigned int>() << endl;
const int ic = 42;
const int* pic = ⁣
const int*& rpic = pic;
cout << type_name<decltype(ic)>() << endl;
cout << type_name<decltype(pic)>() << endl;
cout << type_name<decltype(rpic)>() << endl;
cout << type_name<probe_type>() << endl;
}
Выход
gcc 10.0.0 20190919 Wandbox:
test
const int *&
unsigned int
const int
const int *
const int *&
constexpr std::string_view type_name() [with T = probe_type; std::string_view = std::basic_string_view<char>]
clang 10.0.0 Wandbox:
test
const int *&
unsigned int
const int
const int *
const int *&
std::__1::string_view type_name() [T = probe_type]
VS 2019 версия 16.3.3:
class test
const int*&
unsigned int
const int
const int*
const int*&
class std::basic_string_view<char,struct std::char_traits<char> > __cdecl type_name<class probe_type>(void)
Как объяснил Скотт Мейерс в "Эффективном современном" C++,
Вызовы
std::type_info::name
не гарантированно возвращают что-либо разумное.
Лучшее решение - позволить компилятору генерировать сообщение об ошибке во время вывода типа, например:
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;
}
Результат будет примерно таким, в зависимости от компиляторов,
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;
Следовательно, мы узнаем, что тип x
- это int
, а тип y
- это const int*