是否可以在标准C ++中打印变量的类型?

是否可以在标准C ++中打印变量的类型?

Is it possible to print a variable's type in standard C++?

例如:

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

预期产量:

1
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()之上。

根本问题:

1
typeid(a).name()

扔掉cv-qualifiers,引用和左值/右值。例如:

1
2
3
const int ci = 0;
std::cout << typeid(ci).name() << '
'
;

对我来说输出:

1
i

我猜测MSVC输出:

1
int

const消失了。这不是QOI(实施质量)问题。该标准规定了这种行为。

我在下面推荐的是:

1
template <typename T> std::string type_name();

可以这样使用:

1
2
3
const int ci = 0;
std::cout << type_name<decltype(ci)>() << '
'
;

对我来说:

1
int const

我没有在MSVC上测试过这个。 但我欢迎那些做过的人的反馈。

C ++ 11解决方案

我将ipapadop推荐给非MSVC平台的__cxa_demangle用于demangle类型。但是在MSVC上,我信任typeid来分解名称(未经测试)。这个核心围绕着一些简单的测试,这些测试可以检测,恢复和报告简历限定符以及对输入类型的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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;
}

结果

有了这个解决方案,我可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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)>() << '
'
;
    std::cout <<"decltype((i)) is" << type_name<decltype((i))>() << '
'
;
    std::cout <<"decltype(ci) is" << type_name<decltype(ci)>() << '
'
;
    std::cout <<"decltype((ci)) is" << type_name<decltype((ci))>() << '
'
;
    std::cout <<"decltype(static_cast<int&>(i)) is" << type_name<decltype(static_cast<int&>(i))>() << '
'
;
    std::cout <<"decltype(static_cast<int&&>(i)) is" << type_name<decltype(static_cast<int&&>(i))>() << '
'
;
    std::cout <<"decltype(static_cast<int>(i)) is" << type_name<decltype(static_cast<int>(i))>() << '
'
;
    std::cout <<"decltype(foo_lref()) is" << type_name<decltype(foo_lref())>() << '
'
;
    std::cout <<"decltype(foo_rref()) is" << type_name<decltype(foo_rref())>() << '
'
;
    std::cout <<"decltype(foo_value()) is" << type_name<decltype(foo_value())>() << '
'
;
}

输出是:

1
2
3
4
5
6
7
8
9
10
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表示具有左值引用的左值表达式)。

因此,除了探索和调试自己的代码之外,这个工具还是了解decltype的绝佳工具。

相反,如果我只是在typeid(a).name()上构建它,而不添加丢失的cv限定符或引用,则输出将为:

1
2
3
4
5
6
7
8
9
10
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)。这意味着最新语言功能(如lambdas)的结果更准确。
  • Jamboree的答案并不能完全解决VS的全部问题,我需要对他的代码进行一些调整。但是,由于这个答案得到了很多观点,花一些时间去那里并提出他的答案,没有这个,这个更新永远不会发生。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    #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
    }

    如果您仍然停留在古老的C ++ 11中,此代码将自动退回constexpr。如果您使用C ++ 98/03在洞穴墙上绘画,则noexcept也将被牺牲。

    C ++ 17更新

    在下面的评论中,Lyberta指出新的std::string_view可以取代static_string

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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
    }

    感谢Jive Dadson在下面的评论中所做的非常出色的侦探工作,我已经更新了VS的常数。

    更新:

    请务必查看下面的重写,以消除我最新配方中不可读的幻数。


    尝试:

    1
    2
    3
    4
    5
    #include <typeinfo>

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

    您可能必须在编译器选项中激活RTTI才能使其生效。此外,此输出取决于编译器。它可能是原始类型名称或名称重整符号或介于两者之间的任何内容。


    非常丑陋,但是如果您只想要编译时间信息(例如用于调试),就可以解决问题:

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

    返回:

    1
    2
    3
    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*>'

    不要忘记包含

    我相信您所指的是运行时类型标识。你可以做到这一点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    #include <typeinfo>

    using namespace std;

    int main() {
      int i;
      cout << typeid(i).name();
      return 0;
    }

    请注意,C ++的RTTI功能生成的名称不可移植。
    例如,班级

    1
    MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>

    将具有以下名称:

    1
    2
    3
    4
    // MSVC 2003:
    class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
    // G++ 4.2:
    N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE

    因此,您不能使用此信息进行序列化。但是,仍然可以将typeid(a).name()属性用于日志/调试目的


    您可以使用模板。

    1
    2
    3
    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(和其他一些编译器)中,您可以使用以下代码解决它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #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;

    }


    你可以使用traits类。就像是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #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定义的存在使您可以轻松地为所需的所有类型声明此traits类。

    这可能比涉及typeid的解决方案更有用,因为您可以控制输出。例如,在我的编译器上使用typeid表示long long会给出"x"。


    在C ++ 11中,我们有decltype。标准c ++中无法显示使用decltype声明的确切类型的变量。我们可以使用boost typeindex即type_id_with_cvr(cvr代表const,volatile,reference)来打印如下所示的类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #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() << '
    '
    ;
      cout <<"decltype((i)) is" << type_id_with_cvr<decltype((i))>().pretty_name() << '
    '
    ;
      cout <<"decltype(ci) is" << type_id_with_cvr<decltype(ci)>().pretty_name() << '
    '
    ;
      cout <<"decltype((ci)) is" << type_id_with_cvr<decltype((ci))>().pretty_name() << '
    '
    ;
      cout <<"decltype(std::move(i)) is" << type_id_with_cvr<decltype(std::move(i))>().pretty_name() << '
    '
    ;
      cout <<"decltype(std::static_cast<int&&>(i)) is" << type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name() << '
    '
    ;
      return 0;
    }

    根据霍华德的解决方案,如果您不想要幻数,我认为这是一种很好的表示方式,并且看起来很直观:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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;
    }

    没有函数重载的更通用的解决方案比我以前的解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    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是用户定义的类。这里也可以添加更多条件。

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #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;}

    输出:

    1
    2
    3
    int
    String
    MyClass


    涉及RTTI(typeid)的其他答案可能是您想要的,只要:

    • 您可以负担内存开销(在某些编译器中可能是相当大的)
    • 您的编译器返回的类名是有用的

    备选方案(类似于Greg Hewgill的答案)是建立特征的编译时表。

    1
    2
    3
    4
    5
    6
    7
    8
    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)的模板类型声明名称。

    要访问变量类型的名称,您只需要

    1
    2
    3
    4
    5
    template <typename T>
    const char* get_type_as_string(const T&)
    {
        return type_as_string< T >::value;
    }

    您也可以使用带有选项-t(类型)的c ++ filt来取消类型名称的修饰:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #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上测试过。


    我喜欢尼克的方法,一个完整的表格可能是这样的(对于所有基本数据类型):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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*"; }

    当我挑战时,我决定测试与平台无关(希望如此)的模板欺骗能走多远。

    名称在编译时完全汇编。 (这意味着不能使用typeid(T).name(),因此您必须显式提供非化合物类型的名称。否则,将显示占位符。)

    用法示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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)> << '
    '
    ;
        // -> `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*)> << '
    '
    ;
        // -> `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)> << '
    '
    ;
        // -> `class? (*)(int,??)`
        // With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.
    }

    码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    #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);};

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #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:
    "
    ;
        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:
    "
    ;
                show_type_name(s);
            }
            cout << s << endl;
        }

        int i = 5;
        cout <<"Type of i:" << typeid(i).name() << endl;
        cout <<"Real type of i:
    "
    ;
        show_type_name(i);
        return 0;
    }

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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


    正如Scott Meyers在Effective Modern C ++中所解释的,

    Calls to std::type_info::name are not guaranteed to return anythong sensible.

    最好的解决方案是让编译器在类型推导期间生成错误消息,例如,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;
    }

    结果将是这样,具体取决于编译器,

    1
    2
    3
    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的类型是inty的类型是const int*


    推荐阅读