Можно ли напечатать тип переменной в стандартном C ++? - PullRequest
323 голосов
/ 17 сентября 2008

Например:

int a = 12;
cout << typeof(a) << endl;

Ожидаемый результат:

int

Ответы [ 16 ]

408 голосов
/ 24 ноября 2013

Обновление 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 types. Но на 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 во время компиляции. Это блестящее решение по нескольким причинам:

  1. Это во время компиляции!
  2. Вы получаете сам компилятор, который выполняет работу вместо библиотеки (даже 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 благодаря очень хорошей детективной работе Джайва Дадсона в комментариях ниже.

221 голосов
/ 17 сентября 2008

Попробуйте:

#include <typeinfo>

// …
std::cout << typeid(a).name() << '\n';

Возможно, вам придется активировать RTTI в настройках компилятора, чтобы это работало. Кроме того, вывод этого зависит от компилятора. Это может быть необработанное имя типа или символ искажения имени или что-то среднее между ними.

65 голосов
/ 31 января 2013

Очень некрасиво, но работает, если вам нужна только информация о времени компиляции (например, для отладки):

auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo = 1;

Возвращает:

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*>'
53 голосов
/ 17 сентября 2008

Не забудьте включить <typeinfo>

Я полагаю, что вы имеете в виду идентификацию типа во время выполнения. Вы можете достигнуть вышеупомянутого, делая.

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
  int i;
  cout << typeid(i).name();
  return 0;
}
22 голосов
/ 17 сентября 2008

Обратите внимание, что имена, генерируемые функцией 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 () все еще можно использовать для целей журнала / отладки

18 голосов
/ 17 сентября 2008

Вы можете использовать шаблоны.

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».

14 голосов
/ 31 января 2013

Как уже упоминалось, 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;

}

10 голосов
/ 17 сентября 2008

Вы можете использовать класс черт для этого. Что-то вроде:

#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 существует, чтобы упростить вашу жизнь при объявлении этого класса черт для всех типов, которые вы ожидаете.

Это может быть более полезным, чем решения, включающие typeid, потому что вы можете контролировать вывод. Например, использование typeid для long long в моем компиляторе дает "x".

6 голосов
/ 08 октября 2016

В 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;
}
5 голосов
/ 17 сентября 2008

Другие ответы с участием 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;
}
...