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 是在运行期打印类型信息的可靠工具。