栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

《Effective Modern C++》学习笔记 - Item 4: 了解如何查看推导的类型

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《Effective Modern C++》学习笔记 - Item 4: 了解如何查看推导的类型

(笔者注:本篇的技巧,尤其是关于 typeid 和boost库用法的讲解非常实用)

  • 分三种情形讨论如何获得类型推导的结果:编辑代码时获得,编译时获得和运行时获得。
IDE编辑器
  • 多数现代IDE都有这类功能,当鼠标悬浮在实体上时,显示其类型。
  • 这也说明IDE中通常带有能感知代码信息的编译器,或至少是编译器的前端。
  • 对于简单的类型(如 int),IDE提供的信息很有用。但后面我们会看到,当涉及的类型变复杂后,IDE展示的信息可能并不是非常有帮助。
编译器诊断信息(Diagnostics)
  • 展示推断类型的一种有效方法就是让编译器报与类型相关的错(笔者:读到这儿着实笑了ww)。例如,声明但不定义一个模板类,想查看任何变量的类型,将其传给模板参数即可:
template
class TD;

// x和y的类型会显示在编译错误信息中
TD xType;
TD yType;

运行时输出
  • 问题在于如何创造一个类型的字符串表示(笔者注:大一时这一直是个困扰我的问题,网上学的typeid也不太好用,现在终于得到了一个答案…不过还是要说类型相关的功能至少在使用上,C++确实不如Java, C#, Python等语言方便)
  • 解决方法1:使用C++标准的 typeid。对任意对象使用 typeid 返回一个 std::type_info 对象,该对象有一个成员函数 name(),返回变量类型的 const char* 字符串。name 函数的定义由实现决定,没有统一标准。 例如,MSVC编译器的结果就和GCC不同:

VS2022编译结果。类型的可读性较好。

VSCode使用g++编译结果。i代表int,PKi代表Pointer K(C)onst to int,可读性略差。这些名称源于编译器对类型的内部编码(name mangling机制),可以用 c++filt 工具解码。

  • 以上设定过于简单。让我们向前一步,看看更接近实际应用场景的例子:
// 模板函数,打印T和param的类型名
template
void f(const T& param)
{
    cout << "T =     " << typeid(T).name() << 'n';
    cout << "param = " << typeid(param).name() << 'n';
}
// 某个自定义类
class Widget {};
// 生产Widget数组的工厂函数
vector createVec()
{
    return vector{ Widget() }; // 假设返回的vector含一个元素
}
int main()
{
    const auto vw = createVec();
    if (!vw.empty()) {
        f(&vw[0]);
    }
    return 0;
}

MSVC和GCC运行结果:

PK代表pointer-to-const,6是Widget类名的长度。详见name mangling规则。

  • 问题来了:在模板中,param的类型为 const T&,既然如此,param 和 T 的类型不应该是不同的吗?例如,T 为 int,param 应为 const int&。实际上,两种编译器都告诉我们,param 和 T 的类型均为 const Widget*。
  • 遗憾的是,std::type_info::name 的结果并不可靠,因为它将类型以类似模板按值传递(Case 3)的推断规则处理,这导致类型的引用性、const 和 volatile 都被丢失。实际上,param 的类型应为 const Widget * const &。
  • 同样遗憾的是,IDE展示的类型信息可能同样不靠谱,或至少起不到什么帮助。对于此例,有些IDE将 T 和 param 的类型报告为:
// T
const std::_Simple_types >::_Alloc>::value_type>::value_type *
// param
const std::_Simple_types<...>::value_type *const &

param 看着没那么吓人,但其中的 ... 实际上是缩略。

  • 笔者注:本例中以上问题在我的环境中没有得到复现。目前(2021.12)VS2022和VSCode都报告了正确的函数模板推导结果(不过,我确实有深刻印象VSCode在使用STL类型时经常提示各种大长串难以理解的类型标识符)。

  • 主角登场!作者强力推荐 Boost 库中的 Boost.TypeIndex。以下为使用样例:
#include  // boost库可在官网https://www.boost.org/下载

template
void f(const T& param)
{
	 using std::cout;
	 using boost::typeindex::type_id_with_cvr;
	// show T
	cout << "T = "
		 << type_id_with_cvr().pretty_name()
		 << 'n';
 	// show param's type
	cout << "param = "
		 << type_id_with_cvr().pretty_name()
		 << 'n';
}
  • 这种方法使用了 boost::typeindex::type_id_with_cvr 模板,其接受一个类型参数,并且不会抹除 const、volatile和引用修饰符(因此得名 with_cvr)。返回结果是一个 boost::typeindex::type_index 对象,其有一个成员函数 pretty_name,返回一个容易阅读的 std::string 类型名称字符串。MSVC和GCC运行结果:

总结
  1. 推导出的类型可以通过IDE编辑器,编译错误信息和Boost TypeIndex库查看。
  2. 一些工具的结果可能既不准确也没什么用,因此,我们自身对于C++类型推导规则的理解仍是最重要的。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/664559.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号