例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
例如:
int a = 12;
cout << typeof(a) << endl;
预期的输出:
int
当前回答
正如Scott Meyers在《Effective Modern c++》中所解释的那样,
对std::type_info::name的调用不能保证返回任何有意义的东西。
最好的解决方案是让编译器在类型推断期间生成错误消息,例如:
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;
}
根据不同的编译器,结果会是这样的:
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的类型是int, y的类型是const int*
其他回答
基于之前的一些答案,我做出了这个解决方案,它不将__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);
}
Howard Hinnant使用魔法数字提取类型名称。康桓瑋建议字符串前缀和后缀。但是前缀/后缀一直在变化。 使用" probe_type " type_name自动计算" probe_type "的前缀和后缀大小,以提取类型名称:
#include <string_view>
using namespace std;
namespace typeName {
template <typename T>
constexpr string_view wrapped_type_name () {
#ifdef __clang__
return __PRETTY_FUNCTION__;
#elif defined(__GNUC__)
return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
return __FUNCSIG__;
#endif
}
class probe_type;
constexpr string_view probe_type_name ("typeName::probe_type");
constexpr string_view probe_type_name_elaborated ("class typeName::probe_type");
constexpr string_view probe_type_name_used (wrapped_type_name<probe_type> ().find (probe_type_name_elaborated) != -1 ? probe_type_name_elaborated : probe_type_name);
constexpr size_t prefix_size () {
return wrapped_type_name<probe_type> ().find (probe_type_name_used);
}
constexpr size_t suffix_size () {
return wrapped_type_name<probe_type> ().length () - prefix_size () - probe_type_name_used.length ();
}
template <typename T>
string_view type_name () {
constexpr auto type_name = wrapped_type_name<T> ();
return type_name.substr (prefix_size (), type_name.length () - prefix_size () - suffix_size ());
}
}
#include <iostream>
using typeName::type_name;
using typeName::probe_type;
class test;
int main () {
cout << type_name<class test> () << endl;
cout << type_name<const int*&> () << endl;
cout << type_name<unsigned int> () << endl;
const int ic = 42;
const int* pic = ⁣
const int*& rpic = pic;
cout << type_name<decltype(ic)> () << endl;
cout << type_name<decltype(pic)> () << endl;
cout << type_name<decltype(rpic)> () << endl;
cout << type_name<probe_type> () << endl;
}
输出
gcc 10.2:
test
const int *&
unsigned int
const int
const int *
const int *&
typeName::probe_type
铿锵声11.0.0:
test
const int *&
unsigned int
const int
const int *
const int *&
typeName::probe_type
VS 2019版本16.7.6:
class test
const int*&
unsigned int
const int
const int*
const int*&
class typeName::probe_type
你可以上特质课。喜欢的东西:
#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”。
在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;
}
另一个@康桓瑋的答案(最初),对前缀和后缀细节做了较少的假设,并受到@ 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工作以及。