2012年5月23日星期三

操作符重载中的一个语言设计谜题

This article is translated from Andrew Koenig's blog, if there is any
copyright issue, please contact me, I will remove it as soon as
possible.
原文作者:Andrew Koenig
原文地址:http://www.drdobbs.com/blogs/cpp/240000124#

在上周的文章(关于参数依赖查找的一篇个人笔记)中让我想起早期C++中的一个问题。这问题的解决方案是一条没多少人知道的规则――但是如果没有这条规则的话,很多代码都没法正常运行。
考虑一个表达式a + b,a和b都是类的对象。编译器将其处理为a.opertor+(b)或者operator+(a,
b),然后将每种处理的各种可能性作为一个很大的集合,最后从中来决定如何重载 + 操作符。
函数的重载决议即找到一个唯一的可能函数,其严格的比其他任何一个都要好。更进一步说,即找到一个函数,至少其中的一个参数的匹配比其他的函数都好,同时别的参数的匹配不比别的函数差。比如:

void f(std::string, int);
void f(std:;string, double);
f("foo", 42);

在这里,重载决议将选中第一个函数,因为对于42,int是比double更好的匹配,而对于另一个参数,两个函数都需要用户定义的类型转换。可是,如果我们写下:

void g(std::string, int);
void g(const char*, double);
g("foo", 42);

函数调用将变成模棱两可的,因为对于第一个函数g,第二个参数的匹配度更好,而第二个函数g的第一个参数匹配度更好。
接下来,让我们把例子搞复杂一点点

struct Thing {
void operator+(std::string);
};
void operator+(Thing&, const char*);
Thing t;

然后我们来考虑下对于表达式t + "s"都发生了什么。这个表达式可能被解释为t.operator+("s")或者operator+(t,
"s"),所以编译器必须决定哪一个更好。在两种情况下,左边的参数t的类型都是Thing,所以仅从第一个参数没有理由认为一个函数好于另一个。可是成员形式的operator+需要将第二个参数转换为string,这就意味着非成员形式的operator+能更好的匹配第二个参数。因此,编译器将选择非成员形式。
这一次,我们把例子再搞复杂一点

struct Blog : public Thing { };
Blob b;

然后来看看表达式b + "s"。这给予了你一次扮演语言设计者的机会:看看你是否能搞明白为什么最明显的解释表达式的方式在这里遇到了麻烦,然后再看看你是否能找到一条简洁的语言规则来避免这个麻烦。
我将在下周揭晓答案

没有评论: