例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
当前回答
你可以上特质课。喜欢的东西:
#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定义的存在是为了让您更容易地为所有需要的类型声明这个trait类。
这可能比涉及typeid的解决方案更有用,因为您可以控制输出。例如,在我的编译器上使用typeid For long long会给出“x”。
其他回答
非常丑陋,但如果你只想要编译时信息(例如调试):
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: 'foo' is not a member of 'std::tuple<int, double, const char*>'
一个没有函数重载的更通用的解决方案:
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
根据Howard的解决方案,如果你不喜欢神奇的数字,我认为这是一种很好的表示方式,看起来很直观:
#include <string_view>
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;
}
演示。
我喜欢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*"; }
另一个@康桓瑋的答案(最初),对前缀和后缀细节做了较少的假设,并受到@ val的答案的启发-但没有污染全局名称空间;无条件的:没有任何条件的;希望更容易阅读。
流行的编译器提供了带有当前函数签名的宏。现在,函数是可模板化的;因此签名包含模板参数。因此,基本的方法是:给定一个类型,在函数中使用该类型作为模板参数。
不幸的是,类型名称被包装在描述函数的文本中,这在不同的编译器中是不同的。例如,在GCC中,template <typename T> int foo()具有double类型的签名是:int foo() [T = double]。
那么,如何消除包装器文本呢?@HowardHinnant的解决方案是最简短和最“直接”的:只需使用每个编译器的魔法数字来删除前缀和后缀。但显然,这是非常脆弱的;没有人喜欢在代码中加入神奇的数字。然而,事实证明,给定具有已知名称的类型的宏值,您可以确定构成包装的前缀和后缀。
#include <string_view>
template <typename T> constexpr std::string_view type_name();
template <>
constexpr std::string_view type_name<void>()
{ return "void"; }
namespace detail {
using type_name_prober = void;
template <typename T>
constexpr std::string_view wrapped_type_name()
{
#ifdef __clang__
return __PRETTY_FUNCTION__;
#elif defined(__GNUC__)
return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
return __FUNCSIG__;
#else
#error "Unsupported compiler"
#endif
}
constexpr std::size_t wrapped_type_name_prefix_length() {
return wrapped_type_name<type_name_prober>().find(type_name<type_name_prober>());
}
constexpr std::size_t wrapped_type_name_suffix_length() {
return wrapped_type_name<type_name_prober>().length()
- wrapped_type_name_prefix_length()
- type_name<type_name_prober>().length();
}
} // namespace detail
template <typename T>
constexpr std::string_view type_name() {
constexpr auto wrapped_name = detail::wrapped_type_name<T>();
constexpr auto prefix_length = detail::wrapped_type_name_prefix_length();
constexpr auto suffix_length = detail::wrapped_type_name_suffix_length();
constexpr auto type_name_length = wrapped_name.length() - prefix_length - suffix_length;
return wrapped_name.substr(prefix_length, type_name_length);
}
可以在GodBolt上看到。这应该与MSVC工作以及。