Обновление 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
доверяю разобрать имена (не проверено). И это ядро обернуто вокруг некоторого простого тестирования, которое обнаруживает, восстанавливает и сообщает 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). Это означает более точные результаты для новейших языковых функций (таких как лямбды).
Ответ Джамбори не совсем раскрывает все для 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 благодаря очень хорошей детективной работе Джайва Дадсона в комментариях ниже.
Обновить:
Обязательно ознакомьтесь с этой перепиской ниже, которая устраняет нечитаемые магические числа в моей последней формулировке.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Если вам нужна поддержка кросс-платформенной: Используйте#ifdef
,#else
,#endif
чтобы предоставить один макрос для других платформ , таких как MSVC.