Item 4: Know how to view deduced types.

Item 4: Know how to view deduced types.

这次是对 Effective Modern C++ Item 4 的学习笔记。

从前面的介绍我们知道,类型推导的结果有时候并不是非常明显就能得到,为了安全起见,我们最好能够自己亲自验证下类型推导是否符合我们的预期,在以下3个阶段我们能够获得一些类型推导的信息:

  • 代码编辑阶段
  • 编译阶段
  • 运行阶段

IDE Editors

一般地,在编辑代码时候,你的IDE经常可以展示一些变量、函数、参数等类型。比如如下代码,当你使用鼠标指向 y1y2 时候,会显示 y1int 类型、y2const int* 类型。

#include <iostream>

int main() {
    const int x = 2;
    auto y1 = x;
    auto y2 = &x;
    return 0;
}

IDE 之所以能够知道类型推导的结果,是因为 IDE 背后运行了 C++ 的编译器(至少是编译器的前端),这就要求你的代码或多或少是可编译状态。

对于简单的类型,IDE 的显示的信息通常是有用的,但是对于复杂类型,可能它的推导类型就不准了。

Compiler Diagnostics

可以通过导致编译错误的方式获取类型推导的信息,编译报错信息是很好的提示信息:

#include <iostream>

template<typename T>
class TD;

int main() {
    const int x = 2;
    auto y1 = x;
    auto y2 = &x;

    TD<decltype(y1)> y1Type;
    TD<decltype(y2)> y2Type;

    return 0;
}

上面的代码片段,实例化模板将导致编译报错,报错信息如下:

main.cpp:11:22: error: aggregate ‘TD<int> y1Type’ has incomplete type and cannot be defined
     TD<decltype(y1)> y1Type;
                      ^~~~~~
main.cpp:12:22: error: aggregate ‘TD<const int*> y2Type’ has incomplete type and cannot be defined
     TD<decltype(y2)> y2Type;

从上面的报错信息可以获取到类型推导信息。

Runtime Output

通过允许下时打印类型是比较准确和靠谱的。第一种方式借助 typeid 控制输出:

#include <iostream>

int main() {
    const int x = 2;
    auto y1 = x;
    auto y2 = &x;

    std::cout << "y1: " << typeid(y1).name() << std::endl;
    std::cout << "y1: " << typeid(y2).name() << std::endl;
    return 0;
}

打印信息:

y1: i
y1: PKi

i 代表 int 类型, PKi 代码 pointor to const int

这看起来还好,但是考虑下面的代码:

template<typename T>                // template function to be called
void f(const T& param);

std::vector<Widget> createVec();    // factory function

const auto vw = createVec();        // init vw w/factory return

if (!vw.empty()) {
  f(&vw[0]);                        // call f
  ... 
}

使用 typeid 来输出 Tparam 的类型,不论是哪个编译器,你都会得到错误的结果。例如微软的编译器会告诉你:二者的类型都是 class Widget const*。但是,param 的类型比 T 多一个 const &。这是由于我们这里 typeid 是值传递的,在用模板规则推导类型时,由 Item1 可知会丢失 const T& 中的 const 和引用属性。

幸好还有另一种方法,使用 Boost 库提供的方法,可以得到可靠的类型推导信息:

#include <boost/type_index.hpp>
template <typename T>
void f(const T& param)
{
    using std::cout;
    using boost::typeindex::type_id_with_cvr;
    cout << "T =     "
         << type_id_with_cvr<T>().pretty_name()
         << '\n';
                        `
    cout << "param = "
         << type_id_with_cvr<decltype(param)>().pretty_name();
         << '\n';
    ...`
}

模板函数 boost::typeindex::type_id_with_cvr 使用我们传递的类型实例化,而且它不会删去该类型已有的 const、volatile 和引用语义(所以后缀带有 “with_cvr”)。这个模板函数的返回结果是个boost::typeindex::type_index 对象,该对象的成员函数 pretty_name 会生成非常友好的 std::string来表示这个类型。

GNUClang 的编译器下运行结果为:

T =         Widget const*
param = Widget const* const&

在微软的编译器下运行的结果一致:

T =         class Widget const *
param = class Widget const * const &

总结

  • 通常可以使用 IDE 编辑器、编译器报错信息和 Boost TypeIndex 库来查看已推断的类型。
  • 一些工具的结果可能没有帮助或者不准确,还是要理解透彻 C++ 的类型推断规则。
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值