c++ 模板的类型推导

#include <iostream> #include <boost/type_index.hpp> using namespace std; #define S(expr) do{ cerr << #expr << ":" ; expr; } while(0) template<typename T> void foo1(T x) { cerr << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ";" << boost::typeindex::type_id_with_cvr<decltype(x)>().pretty_name() << " x;" << endl; } template<typename T> void foo2(T& x) { cerr << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ";" << boost::typeindex::type_id_with_cvr<decltype(x)>().pretty_name() << " x;" << endl; } template<typename T> void foo3(const T& x) { cerr << "T = " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ";" << boost::typeindex::type_id_with_cvr<decltype(x)>().pretty_name() << " x;" << endl; } class Widget { public: Widget() {} }; int main(int argc, char *argv[]) { Widget w1; Widget& rw = w1; const Widget cw; const Widget& crw1 = w1; const Widget& crw2 = cw; Widget w2 = cw; // Widget& w3 = cw; S(foo1(w1)); S(foo2(w1)); S(foo3(w1)); cerr << endl; S(foo1(rw)); S(foo2(rw)); S(foo3(rw)); cerr << endl; S(foo1(cw)); S(foo2(cw)); S(foo3(cw)); cerr << endl; S(foo1(crw1)); S(foo2(crw1)); S(foo3(crw1)); return 0; }

输出结果是

foo1(w1):T = Widget;Widget x; foo2(w1):T = Widget;Widget& x; foo3(w1):T = Widget;Widget const& x; foo1(rw):T = Widget;Widget x; foo2(rw):T = Widget;Widget& x; foo3(rw):T = Widget;Widget const& x; foo1(cw):T = Widget;Widget x; foo2(cw):T = Widget const;Widget const& x; foo3(cw):T = Widget;Widget const& x; foo1(crw1):T = Widget;Widget x; foo2(crw1):T = Widget const;Widget const& x; foo3(crw1):T = Widget;Widget const& x;

这里面我们注意到

  • rww1 的推导是完全一样的。也就是说,在模板类型推导的时候,首先忽略到引用。这也是很自然的,引用就是变量的别名,所以他们两个是一样的。同样道理,cwcrw1 的输出也是一样的。
  • 对于 cw ,带有 const 修饰符的,那么就需要模式匹配,也就是把 T 换成 Widget 或者 Widget const ,看哪一个和 Widget const 匹配,哪一个就是 T 的类型。

其他几个不关键的点

  • 宏展开的时候, #expr 可以展成字符串。 stringify
  • 必须指定 Widget() {} 构造函数,否则 const Widget cw 会报错。为什么会报错呢,参考 stack overflow
  • boost::typeindex::type_id_with_cvr 是在运行期打印类型信息的可靠工具。