在channel 9 上看Stephan T. Lavavej讲Core C++(上channel 9搜索 core C+ part II 即可看到),学到了点trick:
如果我们写一段这样的代码,让编译器帮我们做template type deduction:
1 template <typename T> void meow(T val, function<void (T)> f){ 2 f(val); 3 } 4 5 int main(){ 6 meow(1729,[](int x){cout << "lamda " << x << endl;}); 7 }
不会通过编译。因为当编译器将lamda [](int x){cout << "lamda " << x << endl;} 向 function<void (T)> 做template type deduction的时候,会失败,因为C++在做template type deduction的时候不会同时做类型转换。即使那个lamda可以通过类型转换成 function<void (T)> ,但是编译器在这里只会做类型推导,不会同时也做隐式类型转换。
所以,基于以上原因,如果我们要使其能顺利地类型转换通过编译,就应该这样写:
1 template <typename T> void meow(T val, function<void (T)> f) { 2 f(val); 3 } 4 5 int main() { 6 //这里发生了类型转换 7 function<void (int)> fxn = [](int x) { cout << "lamda " << x << endl; }; 8 9 //很自然的template type deduction,因为fxn的类型就是functioin<void (T)>, 这样,T就会被推导为int 10 meow(1729, fxn); 11 }
说好的trick呢 ? 在这里:
1 template <typename T> struct Identity { 2 typedef T type; 3 }; 4 5 template <typename T> void meow(T val, 6 typename Identity<function<void (T)>>::type f) { 7 f(val); 8 } 9 10 int main(){
11 //我可以在这里直接用lamda了 !!Why ?!!! 12 meow(1729,[](int x)(count << "lamda " << x << endl;});
13 }
虽然这段代码有点丑(为了展示这个trick而有意为之),但是还是很有技术含量的。
首先,来看一些编译器在做argument type deduction的流程: 首先是 T val , 由于传入了一个1729, 为int类型,所以此时 T 就“有可能“被推导为int。接着,编译器又看到 typename Identity<function<void (T)>>::type f ,这时候他就不会做类型推导了,原因就是这篇文章题目所说的double column(class access control qualifier) inhibits type deduction:当编译器看到 typename Identity<function<void (T)>::type f 的时候, 由于 Identity<function<void (T)>::type 前面有个 typename ,所以他知道 Identity<function<void (T)>::type 是一个类型,然后当编译器想进一步推导的时候,它看到了 type 这个东西前面有一个 :: (access control qualifier),表明 type 是某个namespace 或某个class里面的东西,所以它就不会再进行类型推导了,也就是说这个 :: 阻止了编译器进一步进行类型推导(所谓 double column inhibits type deduction)。所以,编译器的类型推导到这里就结束了。
那么, Identity<function<void (T)>> 里面的 T 呢? 由于类型推导结束了,那么T的类型自然也就确定了咯,就是在推导 T val 的时候所确定为的 int 。这时候,由于类型推导结束了,那么函数 meow 的参数类型就可以确定为: (int , typename Identity<function<void (int)>>::type ) 了( typename Identity<function<void (T)>>::type 变成了 typename Identity<function<void (int)>>::type ),那么,将lamda [](int x)(count << "lamda " << x << endl;} 传进去就可以正常地发生隐式类型转换了(由lamda转换成 Identity<function<void (int)>>::type ),所以就可以成功调用了。
( source: All the code above are from Stephan T. Lavavej )