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;
这里面我们注意到
rw
和w1
的推导是完全一样的。也就是说,在模板类型推导的时候,首先忽略到引用。这也是很自然的,引用就是变量的别名,所以他们两个是一样的。同样道理,cw
和crw1
的输出也是一样的。- 对于
cw
,带有const
修饰符的,那么就需要模式匹配,也就是把 T 换成Widget
或者Widget const
,看哪一个和Widget const
匹配,哪一个就是T
的类型。
其他几个不关键的点
- 宏展开的时候,
#expr
可以展成字符串。 stringify - 必须指定
Widget() {}
构造函数,否则const Widget cw
会报错。为什么会报错呢,参考 stack overflow boost::typeindex::type_id_with_cvr
是在运行期打印类型信息的可靠工具。