Рассмотрим операторы равенства в нашем классе String более внимательно. Первый оператор позволяет устанавливать равенство двух объектов, а второй – объекта и C-строки:
#include "String.h"
int main() {
String flower;
// что-нибудь записать в переменную flower
if ( flower == "lily" ) // правильно
// ...
else
if ( "tulip" == flower ) // ошибка
// ...
}
При первом использовании оператора равенства в main() вызывается перегруженный operator==(const char *) класса String. Однако на второй инструкции if компилятор выдает сообщение об ошибке. В чем дело?
Перегруженный оператор, являющийся членом некоторого класса, применяется только тогда, когда левым
операндом служит объект этого класса. Поскольку во втором случае левый операнд не принадлежит к классу String, компилятор пытается найти такой встроенный оператор, для которого левым операндом может быть C-строка, а правым – объект класса String. Разумеется, его не существует, поэтому компилятор говорит об ошибке.
Но можно же создать объект класса String из C-строки с помощью конструктора класса. Почему компилятор не выполнит неявно такое преобразование:
if ( String( "tulip" ) == flower ) //правильно: вызывается оператор-член
Причина в его неэффективности. Перегруженные операторы не требуют, чтобы оба операнда имели один и тот же тип. К примеру, в классе Text определяются следующие операторы равенства:
class Text {
public:
Text( const char * = 0 );
Text( const Text & );
// набор перегруженных операторов равенства
bool operator==( const char * ) const;
bool operator==( const String & ) const;
bool operator==( const Text & ) const;
// ...
};
и выражение в main() можно переписать так:
if ( Text( "tulip" ) == flower ) // вызывается Text::operator==()
Следовательно, чтобы найти подходящий для сравнения оператор равенства, компилятору придется просмотреть все определения классов в поисках конструктора, способного привести левый операнд к некоторому типу класса. Затем для каждого из таких типов нужно проверить все ассоциированные с ним перегруженные операторы равенства, чтобы понять, может ли хоть один из них выполнить сравнение. А после этого компилятор должен решить, какая из найденных комбинаций конструктора и оператора равенства (если таковые нашлись) лучше всего соответствует операнду в правой части! Если потребовать от компилятора выполнения всех этих действий, то время трансляции программ C++ резко возрастет. Вместо этого компилятор просматривает только перегруженные операторы, определенные как члены класса левого операнда (и его базовых классов, как мы покажем в главе 19).