Template alias的一个小陷阱

今天碰到一段行为很独特的代码,大意如下:

这段代码在gcc当中无法编译,报错如下:

然而clang/msvc编译这段代码却很正常。哪个编译器的行为正确呢?

从报错信息上来看,gcc认为X<T>和A<T>::U不是一个类型。而肉眼看上去这明明是一个类型。要解释这个行为,可以参考c++标准(17.5.7 temp.alias):

2 When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template

所以,template alias的equivalence是对特化之后的模板才有定义的。换言之,X<int>和A<int>::U是一个类型,然而X<T>和A<T>::U是不是一个类型,标准中并没有定义。然而问题是,在模板特化之前,编译器要先检查模板,确保这个模板定义是well-formed。于是,鉴于标准没有说怎么判定他们一样,g++就认为他们不一样,然后就发脾气了。

问题在于,为什么标准没有定义template alias的equivalence呢?因为边边角角的例子太多,标准委员会表示考虑不过来(#1286, #1979, #1558, #1520, #1244)。举两个例子来说,默认参数不同的别名该不该算作相同的类型(下文代码中的X和Y)?

再比如说,如果有没用的参数怎么办(下文代码中的X<T>和Y<T,U>)?

等等等等。所以就干脆不定义类型的相同了。

然而这样的结果就是开头那个看上去很直觉的程序还是编译不过,标准委员会计划定义一个简单一些的别名等价来回避这些复杂的问题,不过根据委员会历来的效率,感觉大概要2027年才能吵完这个问题吧。

傻瓜

我曾经很讨厌病毒式营销——倒不是因为内容有多不靠谱,而是这种营销方式给我一种被利用的感觉,让我感觉自己像个傻瓜。所以呢,看到绝大多数有分享冲动的内容,我都会思考或是搜索一下,当我兴高采烈的时候,是否有一个阴暗的反派人物在墙角冷笑:这个2B;如果有的话,就会条件反射的站远一些。

有一次和好友讲起来这个心态,朋友说,如果没什么坏处的话,其实被利用一下又如何呢?我语塞好久。

小的时候会做很多天真的事,也有很多天真的想法。随着年龄增长慢慢的意识到这些事这些念头很蠢。就经常回忆起之前生活的片段或是念头,骂一句:“这个2B”,琢磨着当时在场的熟人或者陌生人会怎么嘲笑或者鄙视自己,然后竭力避免再做蠢事。

慢慢的就矫枉过正,很在意自己有没有在犯蠢或者中二,很在意别人脑海里对自己的印象如何,或是会不会被人卖了还帮人数钱,是不是因为一句简单的鸡汤就去奋不顾身。觉得仿佛避免这些事情,在脑海里回忆自己的过去的时候就会更加满意,骂自己“真是2B”的时刻就会少一些。

后来逐渐意识到,无论如何,我终究是一个比较尴尬笨拙的人。面对这个光怪陆离的世界和各路高等级玩家,注定只能做那个进了大观园的刘姥姥,接受自己是个乡下人的事实:偶尔感慨两句“还有这种操作”是可以的,自己硬要学同样的操作,那只能成为“我能反杀”的新标本而已。

反过来,就像那位朋友所说,好像傻一点也没什么不好。如果选不对正确的路,就拿出更多的勇气坚持不怎么聪明的选择;如果无论如何都很笨拙,就大方一点给围观群众提供一点欢乐;努力去相信世界和他人的善意,相信看上去令人不满的事往往只是无心或是不得已而为之。

于是就努力生活的简单一点,或许现实的世界复杂多变,不再能扮演一个无知无畏的孩子,那就微笑着做一个清醒的笨蛋。值得庆幸的是,无论曾经或是现在的我有多么任性或是天真,仍然有很多人愿意包容一个固执中二的傻瓜。谢谢你们。

不管我们离从前有多遥远,至少还可以尝试寻找那个偏执的少年。