java如何打印出变量类型_是否可以在标准C ++中打印变量的类型?

例如:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值