例如:
int a = 12;
cout << typeof(a) << endl;
预期产量:
int
#1楼
非常丑陋,但是如果您只想要编译时间信息(例如,用于调试),则可以达到目的:
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'
#2楼
如前所述, typeid().name()可能返回错误的名称。 在GCC(和其他一些编译器)中,您可以使用以下代码来解决它:
#include
#include
#include
#include
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;
}
#3楼
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限定词,引用和左值/右值-ness。 例如:
const int ci = 0;
std::cout << typeid(ci).name() << '\n';
对我来说输出:
i
我猜对MSVC输出:
int
即const不见了。 这不是QOI(实施质量)问题。 该标准规定了这种行为。
我在下面推荐的是:
template std::string type_name();
可以这样使用:
const int ci = 0;
std::cout << type_name() << '\n';
对我来说:
int const
我尚未在MSVC上对此进行测试。 但是,我欢迎那些提供反馈的人。
C ++ 11解决方案
根据ipapadop在他对demangle类型的回答中的建议,我正在将__cxa_demangle用于非MSVC平台。 但是在MSVC上,我相信typeid可以拆解名称(未经测试)。 这个核心围绕着一些简单的测试,这些测试可以检测,恢复和报告简历限定符以及对输入类型的引用。
#include
#include
#ifndef _MSC_VER
# include
#endif
#include
#include
#include
template
std::string
type_name()
{
typedef typename std::remove_reference::type TR;
std::unique_ptr 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
::value)r += " const";
if (std::is_volatile
::value)r += " volatile";
if (std::is_lvalue_reference::value)
r += "&";
else if (std::is_rvalue_reference::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() << '\n';
std::cout << "decltype((i)) is " << type_name() << '\n';
std::cout << "decltype(ci) is " << type_name() << '\n';
std::cout << "decltype((ci)) is " << type_name() << '\n';
std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n';
std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n';
std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n';
std::cout << "decltype(foo_lref()) is " << type_name() << '\n';
std::cout << "decltype(foo_rref()) is " << type_name() << '\n';
std::cout << "decltype(foo_value()) is " << type_name() << '\n';
}
输出为:
decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast(i)) is int&
decltype(static_cast(i)) is int&&
decltype(static_cast(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限定词或引用,则输出将是:
decltype(i) is int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast(i)) is int
decltype(static_cast(i)) is int
decltype(static_cast(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(foo_value()) is int
即剥夺了每个参考和简历限定词。
C ++ 14更新
只是当您认为自己已确定解决问题的方法时,总会有人无所事事,向您展示更好的方法。 :-)
来自Jamboree的 答案显示了如何在编译时获取C ++ 14中的类型名称。 这是一个出色的解决方案,其原因如下:
在编译时!
您可以让编译器本身代替库(甚至是std :: lib)来执行此工作。 这意味着使用最新的语言功能(例如lambda)可获得更准确的结果。
Jamboree的 答案并不能完全解决VS的全部问题,我需要对他的代码进行一些调整。 但是,由于这个答案得到了很多人的认可,因此,请花一些时间浏览该地点并投票支持他的答案,否则,这个更新就永远不会发生。
#include
#include
#include
#include
#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 <: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<
{
return os.write(s.data(), s.size());
}
template
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 ++ noexcept在洞穴墙壁上绘画,那么noexcept也将被牺牲。
C ++ 17更新
在下面的评论中, Lyberta指出新的std::string_view可以代替static_string :
template
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的常数。
更新:
请确保在下面检查此重写,以消除我最新公式中无法理解的幻数。
#4楼
我喜欢尼克的方法,一个完整的表格可能是这样的(对于所有基本数据类型):
template 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*"; }
#5楼
一个比我以前的解决方案更通用的解决方案,没有函数重载:
template
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same::value) Type="int";
if(std::is_same::value) Type="String";
if(std::is_same::value) Type="MyClass";
return Type;}
MyClass是用户定义的类。 也可以在此处添加更多条件。
例:
#include
class MyClass{};
template
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same::value) Type="int";
if(std::is_same::value) Type="String";
if(std::is_same::value) Type="MyClass";
return Type;}
int main(){;
int a=0;
std::string s="";
MyClass my;
std::cout<
std::cout<
std::cout<
return 0;}
输出:
int
String
MyClass