今天踩坑模板类型推导。

本来是想通过设定bits的长度来推导出采用的类型,比如bits <= 32就使用unsigned int,超过32位就采用unsigned long这种类型。多年不写cpp,好容易搞了个type_trait设计。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <unsigned bits, bool u32 = bits <= 32>
struct bits_type {};

template <unsigned bits>
struct bits_type<bits, true> {
	using type = unsigned int;
};

template <unsigned bits>
struct bits_type<bits, false> {
	using type = unsigned long;
};

So far so good。结果栽倒函数模板上

1
2
3
4
5
template <unsigned bits, typename T = typename bits_type<bits>::type>
void insert(T prefix) {
}

insert<64>(0);

结果死活给我推导出来T = int,思索好久才明白,傻逼了,搞混了模板参数的默认值和模板函数的参数类型推导。

这里T的默认类型是通过bits_type推导得到,但是并没有什么卵用,因为函数模板的参数类型是通过调用函数的实际值推导出来的,这里insert<64>(0)的参数就是一个int参数0而已。当然此时是T=int,而不是默认模板参数T=unsigned long

因此,正常的写法应该是

1
2
3
template <unsigned bits>
void insert(typename bits_type<bits>::type prefix) {
}

后来我觉得整个这玩意基本属于脱裤子放屁,直接写成了

1
2
3
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
void insert(T prefix) {
}

也不需要人去指定到底bits多长了,通过参数就知道了啊。

写下这个,感觉是对于cpp,我感觉基本没机会完全搞明白了,草台班子,大家凑合用吧。就是突然感觉如果大型系统都只能靠凑合用的方式搞定,岂不是很不稳定。但是想想看,Linux用纯C,难道纯C是稳定基础?感觉也未必,那些天天黑cpp的人,大概是没看到近几年,操作系统有fuchsia,内核都用cpp写了,ML系统有tensorflow也是cpp写的,GPU Cuda都是cpp写的。时间长一点,估计慢慢大家也就习惯了。

有人说,咋不去用golang。感觉golang确实是个好东西。于是突然顿悟了,实际上做单体底层服务,C/C++基本跑不掉。但是如果是微服务/分布式啥的,确实应该加强下网络I/O方面的能力,此时goroutine真的是好东西。我曾经想用golang写个发包程序,后面发现为了优化性能,各种hack golang底层,后面觉得很没意思,感觉是用golang的不擅长的东西去死磕。从兼容C的角度看,C++确实很值得尝试,但是从项目组织的角度看,还是算了吧。大家更容易在C上达成一致。

有人说,那应该去搞搞Rust。Rust真的是,人见人夸,人见人爱。一般对这种东西,我都很警惕。因为一般说法是,Worse is better,被广泛采用的技术,一般都是誉满天下,谤满天下。哪有一边倒说好的。别的不说,rust加入编译器lifetime检查之后,编译速度肯定会被诟病。现在Cpp的编译速度就被喷到死了,Rust如果真的被大规模采用,必然被狂喷。我等着这一天。

结论,如果要写网络app,选golang或者java。如果要搞底层,又想偷懒,美其名曰提高开发效率,那只能cpp没跑了,Rust没接触,估计如果靠他一直吹的内存安全性的marketing能说服领导的话,也是能搞搞的。

结论,组织越大,其实就应该降低选择空间,比如C/golang,没有这么多fancy的东西,很多时候,实现只有一种方式,更容易达成一致;如果组织越小,就应该倾向于提高单体生产力。但是组织一向是发展的,人员和问题规模变化之后,一切都会变化。最近的感受是,很多工程师感觉大公司里,代码不重要,感觉这是不重视技术。有时候,我反而觉得,确实不重要。最近又重温了Mike Acton在2014年cppcon上的演讲,确实很受启发。有的说法是,codebase很重要,祖传代码,积淀而来。但是实际上是,硬件在变,规模在变,为了代码复用,忽略变化,其实是无奈之举。很多时候,不是说不该推倒重来,而是推倒重来的代价太大,从商业上划不来,而只能故步自封。当时有人在问,如果都按照你这样扣硬件,当你的平台从PS3升级到PS4,你如何复用你的代码呢?他回答,“毕竟你的问题是,硬件平台从PS3升级到PS4,你要解决的是这个问题,而不是复用之前的,可能低效的代码。所以重写总是不可避免的。”