[声明]:本英文资料源自于Herb Sutter 创建的“Conversation”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter、Jim Hyslop同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。
文章来源:http://www.gotw.ca
版权归属:Herb Sutter and Jim Hyslop
译 者:amature(CSDN)
对话#21:模板特化,缺省参数和其他一些有趣的事情
我们监视着不远处的大门,生怕从木卫二基地尾随而来的入侵者会跟进来。但看来他们很满足于让我们呆在里面,或至少等我们自己出来。大门那边依然静悄悄一片。
不管我们是多么警戒,大笨蛋们仍然占领了我们的许多岗哨。我们杀死了很多大笨蛋-有时是依靠我们的常规武器,有时是外星文物,最起码其中之一可以喷射出高能闪电使得在它周围想瞄准的任何东西都是非常危险的-但是大笨蛋们似乎是源源不断地产生的,怎么也杀不完。
因此兵变并不是我们唯一担心的东西。
- - - - - - - - - - - - - - - - - - - - - - - - -
“嘿,伙计,事情进展得怎样了?"听到鲍勃的声音,我有点紧张。然而令我吃惊的是-鲍勃没有摆出他那一贯的高人一等的姿态。他非常地....恕我直言...平易近人。
"还不坏吧,鲍勃,"我答了一声,同时也转过身来面对他,我们互相凝视了一会。
"嗯,伙计,我想知道你是否愿意帮我一个忙。"
我大吃一惊,哑然无语。这个鲍勃,是部门里最差的程序员,热衷于写晦涩的、难于维护的代码,偏偏却自命不凡,你在一个街区以外就能感受到他那股傲慢劲。他只有在把一大沓问题扔给我(同时把他的咖啡放在我的书桌上)时才造访我的蜗居,而现在,就这么一个人,竟然站在我的小屋里,请求我的帮助!
“啊哈,当然可以,鲍勃,”我最后回到道,“出了什么问题?”
鲍勃自己坐到了客椅上。"OK,问题就在这里面。 我已经设法把它浓缩成这段源代码”,他拿过我的键盘并从我的工作站里打开了一个文件。我仔细地看着它:
#include <iostream>
template<typename T, std::size_t size = 10>
class c
{
T m[size];
public:
void print_size()
{
std::cout << size << std::endl;
}
};
template<> class c<char>
{
char m[100];
public:
void print_size()
{
std::cout << 100 << std::endl;
}
};
int main()
{
c<char>().print_size();
c<char, 10>().print_size();
}
“首先,我不明白它怎能通过编译。我的意思是,我认为模板的参数必须是一个类或类型的名字,而不是一个普通参数。”
我问道:“你是指参数 size_t ?”鲍勃咕哝了一声,我知道他是同意我刚才的问题。“是的,当我第一次看到一个无类型参数的时候,我的大脑也停顿了一会。我习惯于看到 class 或 typename 声明而不是通常的参数。Guru肯定这是允许的,但她让我自己到标准里去查找。你只能使用整型或枚举型,对象或函数的指针或引用,或者是指向成员的指针 [1] 。实际上,对于整数类型,参数变成了一个整型常量表达式...”
“~!@#$%^&*?”鲍勃打断了我。
“一个编译时常量,”我将他的嚎叫翻译成人话。“总之,由于它是整数-哦,也就是说由于它是编译时常量,就允许你用 size 来声明对象 T 的数组。相当灵活。”
“自从我学会了这一点,我经常玩弄这样一个念头:使用一个模板来提供一种更加灵活的 switch 语句,就像,”我打开了一个新的文件,键入下列文字:
template<int value1, int value2, int value3>
void f(int currentValue)
{
switch(currentValue)
{
case value1:
...
case value2:
...
case value3:
...
}
}
void g()
{
f<1, 2, 3>(1); // 执行 value1 情形
f<2, 3, 1>(1); // 执行 value3 情形
}
“我不能拿出一个使用它的实际程序,所以我把它归入‘相当不错,但尚未实际应用’这一类。”
“除了其它一些无用的信息,在集会上还提出了一些东西,毫无疑问,”鲍勃打断了我,扫了一眼他的手表,“哦,伙计,那相当有趣,但是回到我的问题”。他把身体凑过来,关掉了我的文本窗口并重新打开他先前的程序。“看看,当我编译并运行它后,第一个模板打印出了100,正如我所预料的,嗯,因为它实例化了那个c<char> 的特化。我期望第二个模板打印出10,因为我认为用10和那个缺省参数10相匹配,但它也打印出了100。”
我研究着代码。鲍勃呷了一口咖啡。
“那好,”我咳了一下来打破沉寂,“我的第一个反应就是我期望这两个模板都打印出10。”
“你知道,那是我告诉那个三八的,-哦,我是说我首先想到的。”啊,这更像我所知道的那个鲍勃-他会用一个下流的名字来称呼Guru。
“我设法把缺省参数想为‘懒惰的捷径’,”我继续道,“每当我利用缺省参数来东西的时候,我不得不记住编译器把它当做我实际上已经写了参数来对待。”
“我知道,”鲍勃没有耐心地说道。他拿过键盘并且对程序作了轻微的修改:
int main()
{
c<char>().print_size();
// compiler acts as if I written
// c<char, 10>().print_size();
c<char, 10>().print_size();
}
“对于基模板,这是一个完全匹配。在那种情况下,对于每个size,程序将打印出10,但是它却打印出了100,那么是什么出了问题呢?”鲍勃不耐烦地问道,同时扫了一眼他的手表。
我尽情享受这一刻。不仅因为鲍勃明白我了解一些他不知的东西,而且实际上他正承认这一点。
“那并不是缺省参数使用的唯一地方,”我最后说。鲍勃给了我一个询问性的眼光。“特化也使用它。”我拿过键盘,小心地将鲍勃的咖啡移到了别的地方。“你将特化写成...”
template<> class c<char>
“...像你写的那样运行。”
template<> class c<char, 10>
“因此,”我继续道,“你不是对c<char> 特化,而是对 c<char, 10>特化,那里,模板参数size 是一个整型常量表达式,其值为10。换句话说,你的程序就像下面写的那样运行:”
#include <iostream>
template<typename T, std::size_t size = 10>
class c
{
T m[size];
public:
void print_size()
{
std::cout << size << std::endl;
}
};
template<> class c<char, 10>
{
char m[100];
public:
void print_size()
{
std::cout << 100 << std::endl;
}
};
int main()
{
c<char, 10>().print_size();
c<char, 10>().print_size();
}
“我明白了,”鲍勃一边微笑一边在他的椅子上向后伸展。“在 main(), c 的两个实例在实例化中都进行了特化。”我不喜欢他脸上的笑容。他站起来,用很大的嗓门继续道,“伙计,你也要记住应用于模板特化的缺省参数。”我被他的突然改变迷惑了。我发现Guru正在走来,手里像往常一样拿着一本打开的厚书。
她在鲍勃的前面停了下来:“你为什么要打断我徒弟的投入?”Guru询问道。
“嘿,娃娃脸,”鲍勃对着Guru微笑道,“我刚刚在这里是向小伙子解释为什么我们所讨论的程序的正确输出是100 100。”他瞥了一下手表,“是,快要到2点了-我最好去彼得的办公室并和他讨论这个。”
Guru和我目送他消失在去彼得.威廉姆斯的办公室的方向。彼得是我们公司上个月被并购后的新管理者。
“他领会到了吗?”Guru咕哝着转向我,“现在事情更糟了。”我的胃一阵抽搐。
“嗯,这全是为了什么?”我问道。
Guru疲倦地坐在客椅上。鲍勃走了,她也恢复了“正常模式”:“你曾经问过我,鲍勃为什么仍然在这里,尽管他是一个...",看得出来,她努力地想找到合适的词语。
“无能、傲慢、讨厌女人...”我补充道,Guru把这些概括到一个词,我绝不敢把这个词复述给我妈妈,哪怕她是个聋子。
“是的,很正确,”Guru继续道,现在她平静下来了,“鲍勃和以前的管理层保持着很密切的联系。他不知通过什么方法使他们被蒙蔽地认为他是个很好地...极其好的男孩。我想暴露他的真实本性的所有努力都被扫到了一边,因为他使上面深信我仍然痛苦于我们的离婚。好像!”
“总之,”她继续道,“当我们的管理层进行变化的时候,我发现了一个极好的机会。我说服彼得.威廉姆斯,扔给他一个有挑战性的问题,这问题鲍勃肯定无法解决,我是这么认为的,而彼得也同意这个问题是极其简单的,即使让你来回答也不会有任何问题。”我被刺痛了,但是正专注意解释的Guru没顾及我的反应,“如果鲍勃不能在两点前找到答案,彼得将会密切关注他,或许甚至将他放到实习的位置上。最终,我坚信可以使鲍勃得到他应得的-一张解雇通知书。可是现在,这将变得更加艰难。”
她叹息,悄悄地站起来,无声地走开。
我的胃酸涌过了喉咙里。“我做了什么?”我朝自己嘀咕着。
- - - - - - - - - - - - - - - - - - - - - - - - -
就在黎明前,反叛者行动了,好几个军官全副武装地走进了达罗萨的帐篷,同时,我们很快发现只有反叛的军官们有着能使用的武器。达罗萨没有做真正的抵抗,我想他很疲乏而且已经听天由命,觉得再抵抗也是徒劳,于是乖乖缴械,辛克尔接过指挥权,我们准备投降。
一个人被派出去向大门里投了一封投降信。当信被拿起的时候,通过大门我们可以看到一个短暂的移动,不久传来了答复。依照指示,我们放下了武器,一个接一个地列队通过大门。轮到我时,我走进了那个昏暗的跳舞场,转身发现自己凝视着表情冷酷的人们手里所拿着的武器。我叹息,和其他人一样被绑了起来。
[参考文献]
[1] ISO C++ Standard, IOS/IEC 14882, clause 14.1 paragraph 4.
[感谢]
感谢sergei(sergei@summertime.mtu...)在comp.lang.c++上对问题的回答,才能产生这篇文章。
[关于作者]
Herb Sutter
是个独立顾问,也是ISO/ANSI C++标准委员会的秘书。你可通过hsutter@acm.org.联系他
Jim Hyslop
Leitch Technology International Inc.资深的软件设计师,你可通过jim.hyslop@leitch.com联系他