例如:

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

预期的输出:

int

当前回答

一个没有函数重载的更通用的解决方案:

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

其他回答

当我提出挑战时,我决定测试一下与平台无关的模板技巧能走多远。

名称在编译时完全组装。(这意味着不能使用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's 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);};

对于一些不同的东西,这里有一个类型的“To English”转换,解构每个限定符、范围、参数等等,递归地构建描述类型的字符串,我认为“演绎这个”建议将有助于减少许多特殊化。无论如何,这是一个有趣的晨练,尽管过度膨胀。:)

struct X {
    using T = int *((*)[10]);
    T f(T, const unsigned long long * volatile * );
};

int main() {

    std::cout << describe<decltype(&X::f)>() << std::endl;
}

输出:

pointer to member function of class 1X taking (pointer to array[10]
of pointer to int, pointer to volatile pointer to const unsigned 
long long), and returning pointer to array[10] of pointer to int

代码如下: https://godbolt.org/z/7jKK4or43

注:最新版本在我的github: https://github.com/cuzdav/type_to_string

// Print types as strings, including functions, member 

#include <type_traits>
#include <typeinfo>
#include <string>
#include <utility>

namespace detail {

template <typename T> struct Describe;

template <typename T, class ClassT> 
struct Describe<T (ClassT::*)>  {
    static std::string describe();
};
template <typename RetT, typename... ArgsT> 
struct Describe<RetT(ArgsT...)>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...)>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) volatile>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) volatile noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...)&>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const &>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) volatile &>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) & noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile &>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const & noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) volatile & noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile & noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) &&>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const &&>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) volatile &&>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) && noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile &&>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const && noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) volatile && noexcept>  {
    static std::string describe();
};
template <typename RetT, class ClassT, typename... ArgsT> 
struct Describe<RetT(ClassT::*)(ArgsT...) const volatile && noexcept>  {
    static std::string describe();
};

template <typename T>
std::string describe()
{
    using namespace std::string_literals;
    auto terminal = [&](char const * desc) {
        return desc + " "s + typeid(T).name();
    };
    if constexpr(std::is_const_v<T>) {
        return "const " + describe<std::remove_const_t<T>>();
    }
    else if constexpr(std::is_volatile_v<T>) {
        return "volatile " + describe<std::remove_volatile_t<T>>();
    }
    else if constexpr (std::is_same_v<bool, T>) {
        return "bool";
    }
    else if constexpr(std::is_same_v<char, T>) {
        return "char";
    }
    else if constexpr(std::is_same_v<signed char, T>) {
        return "signed char";
    }
    else if constexpr(std::is_same_v<unsigned char, T>) {
        return "unsigned char";
    }
    else if constexpr(std::is_unsigned_v<T>) {
        return "unsigned " + describe<std::make_signed_t<T>>();
    }
    else if constexpr(std::is_void_v<T>) {
        return "void";
    }
    else if constexpr(std::is_integral_v<T>) {
        if constexpr(std::is_same_v<short, T>) 
            return "short";
        else if constexpr(std::is_same_v<int, T>) 
            return "int";
        else if constexpr(std::is_same_v<long, T>) 
            return "long";
        else if constexpr(std::is_same_v<long long, T>) 
            return "long long";
    }
    else if constexpr(std::is_same_v<float, T>) {
        return "float";
    }
    else if constexpr(std::is_same_v<double, T>) {
        return "double";
    }
    else if constexpr(std::is_same_v<long double, T>) {
        return "long double";
    }
    else if constexpr(std::is_same_v<std::nullptr_t, T>) { 
        return "nullptr_t";
    }
    else if constexpr(std::is_class_v<T>) {
        return terminal("class");
    }
    else if constexpr(std::is_union_v<T>) {
        return terminal("union");
    }
    else if constexpr(std::is_enum_v<T>) {
        std::string result;
        if (!std::is_convertible_v<T, std::underlying_type_t<T>>) {
            result += "scoped ";
        }
        return result + terminal("enum");
    }  
    else if constexpr(std::is_pointer_v<T>) {
        return "pointer to " + describe<std::remove_pointer_t<T>>();
    }
    else if constexpr(std::is_lvalue_reference_v<T>) {
        return "lvalue-ref to " + describe<std::remove_reference_t<T>>();
    }
    else if constexpr(std::is_rvalue_reference_v<T>) {
        return "rvalue-ref to " + describe<std::remove_reference_t<T>>();
    }
    else if constexpr(std::is_bounded_array_v<T>) {
        return "array[" + std::to_string(std::extent_v<T>) + "] of " +
            describe<std::remove_extent_t<T>>();
    }
    else if constexpr(std::is_unbounded_array_v<T>) {
        return "array[] of " + describe<std::remove_extent_t<T>>();
    }
    else if constexpr(std::is_function_v<T>) {
        return Describe<T>::describe();
    }
    else if constexpr(std::is_member_object_pointer_v<T>) {
        return Describe<T>::describe();
    }
    else if constexpr(std::is_member_function_pointer_v<T>) {
        return Describe<T>::describe();
    }
}

template <typename RetT, typename... ArgsT> 
std::string Describe<RetT(ArgsT...)>::describe() {
    std::string result = "function taking (";
    ((result += detail::describe<ArgsT>(", ")), ...);
    return result + "), returning " + detail::describe<RetT>();
}

template <typename T, class ClassT> 
std::string Describe<T (ClassT::*)>::describe() {
    return "pointer to member of " + detail::describe<ClassT>() +
        " of type " + detail::describe<T>();
}

struct Comma {
    char const * sep = "";
    std::string operator()(std::string const& str) {
        return std::exchange(sep, ", ") + str;
    }
};
enum Qualifiers {NONE=0, CONST=1, VOLATILE=2, NOEXCEPT=4, LVREF=8, RVREF=16};

template <typename RetT, typename ClassT, typename... ArgsT>
std::string describeMemberPointer(Qualifiers q) {
    std::string result = "pointer to ";
    if (NONE != (q & CONST)) result += "const ";
    if (NONE != (q & VOLATILE)) result += "volatile ";
    if (NONE != (q & NOEXCEPT)) result += "noexcept ";
    if (NONE != (q & LVREF)) result += "lvalue-ref ";
    if (NONE != (q & RVREF)) result += "rvalue-ref ";
    result += "member function of " + detail::describe<ClassT>() + " taking (";
    Comma comma;
    ((result += comma(detail::describe<ArgsT>())), ...);
    return result + "), and returning " + detail::describe<RetT>();
}

template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...)>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(NONE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(CONST | VOLATILE | NOEXCEPT);
}

template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) &>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const &>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) & noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile &>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile & noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile &>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const & noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile & noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(LVREF | CONST | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...)&&>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const &&>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) && noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile &&>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) volatile && noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | VOLATILE | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile &&>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST | VOLATILE);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const && noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST | NOEXCEPT);
}
template <typename RetT, class ClassT, typename... ArgsT> 
std::string Describe<RetT(ClassT::*)(ArgsT...) const volatile && noexcept>::describe() {
    return describeMemberPointer<RetT, ClassT, ArgsT...>(RVREF | CONST | VOLATILE | NOEXCEPT);
}

} // detail

///////////////////////////////////
// Main function
///////////////////////////////////
template <typename T>
std::string describe() {
    return detail::describe<T>();
}


///////////////////////////////////
// Sample code
///////////////////////////////////
#include <iostream>


struct X {
    using T = int *((*)[10]);
    T f(T, const unsigned long long * volatile * );
};

int main() {
    std::cout << describe<decltype(&X::f)>() << std::endl;
}

c++在编译时使用模板和运行时使用TypeId进行数据类型解析。

编译时解决方案。

template <std::size_t...Idxs>
constexpr auto substring_as_array(std::string_view str, std::index_sequence<Idxs...>)
{
  return std::array{str[Idxs]..., '\n'};
}

template <typename T>
constexpr auto type_name_array()
{
#if defined(__clang__)
  constexpr auto prefix   = std::string_view{"[T = "};
  constexpr auto suffix   = std::string_view{"]"};
  constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
  constexpr auto prefix   = std::string_view{"with T = "};
  constexpr auto suffix   = std::string_view{"]"};
  constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
  constexpr auto prefix   = std::string_view{"type_name_array<"};
  constexpr auto suffix   = std::string_view{">(void)"};
  constexpr auto function = std::string_view{__FUNCSIG__};
#else
# error Unsupported compiler
#endif

  constexpr auto start = function.find(prefix) + prefix.size();
  constexpr auto end = function.rfind(suffix);

  static_assert(start < end);

  constexpr auto name = function.substr(start, (end - start));
  return substring_as_array(name, std::make_index_sequence<name.size()>{});
}

template <typename T>
struct type_name_holder {
  static inline constexpr auto value = type_name_array<T>();
};

template <typename T>
constexpr auto type_name() -> std::string_view
{
  constexpr auto& value = type_name_holder<T>::value;
  return std::string_view{value.data(), value.size()};
}

运行时的解决方案。

template <typename T>
void PrintDataType(T type)
{
    auto name = typeid(type).name();
    string cmd_str = "echo '" + string(name) + "' | c++filt -t";
    system(cmd_str.c_str());
}

主要代码

#include <iostream>
#include <map>
#include <string>
#include <typeinfo>
#include <string_view>
#include <array>   // std::array
#include <utility> // std::index_sequence
using std::string;

int main () { / /动态分辨率。 std::map<int, int> iMap; PrintDataType (iMap); //编译类型解析。 std:: cout < < type_name < std::列表< int > > () < < std:: endl; 返回0; }

代码片段

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

您可以承担内存开销(对于某些编译器,这可能相当大) 编译器返回的类名很有用

另一种选择(类似于Greg Hewgill的答案)是建立一个特征的编译时表。

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;
}

基于之前的一些答案,我做出了这个解决方案,它不将__PRETTY_FUNCTION__的结果存储在二进制文件中。它使用静态数组保存类型名称的字符串表示形式。

它需要c++ 23。

#include <iostream>
#include <string_view>
#include <array>

template <typename T>
constexpr auto type_name() {
    auto gen = [] <class R> () constexpr -> std::string_view  {
        return __PRETTY_FUNCTION__;
    };
    constexpr std::string_view search_type = "float";
    constexpr auto search_type_string = gen.template operator()<float>();
    constexpr auto prefix = search_type_string.find(search_type);
    constexpr auto suffix = search_type_string.size() - prefix - search_type.size();
    constexpr auto str = gen.template operator()<T>();
    constexpr int size = str.size() - prefix - suffix;
    constexpr auto static arr = [&]<std::size_t... I>(std::index_sequence<I...>) constexpr {
        return std::array<char, size>{str[prefix + I]...};
    } (std::make_index_sequence<size>{});

    return std::string_view(arr.data(), size);
}