Стандарты программирования на С++. 101 правило и рекомендация | страница 71



, в рекомендации 54.

Примеры

Пример 1. Перегрузка. Пусть у нас есть, например, >Widget::Widget(unsigned int), который может быть вызван неявно, и функция >Display, перегруженная для >Widget и >double. Рассмотрим следующий сюрприз при разрешении перегрузки:

>void Display(double);        // вывод double

>void Display(const Widget&); // Вывод Widget


>Display(5);                  // гм! Создание и вывод Widget

Пример 2. Работающие ошибки. Допустим, вы снабдили класс >String оператором operator >const char*:

>class String {

> // ...

>public:

> operator const char*(); // Грустное решение...

>};

В результате этого становятся компилируемыми масса глупостей и опечаток. Пусть >s1 и >s2 — объекты типа >String. Все приведенные ниже строки компилируются:

>int x = s1 - s2;        // Неопределенное поведение

>const char* р = s1 - 5; // Неопределенное поведение

>р = s1 + '0';           // делает не то, что вы ожидаете

>if (s1 == "0") { ... }  // делает не то, что вы ожидаете

Именно по этой причине в стандартном классе >string отсутствует >operator const char*.

Исключения

При нечастом и осторожном использовании неявные преобразования типов могут сделать код более коротким и интуитивно более понятным. Стандартный класс >std::string определяет неявный конструктор, который получает один аргумент типа >const char*. Такое решение отлично работает, поскольку проектировщики класса приняли определенные меры предосторожности.

• Не имеется автоматического преобразования >std::string в >const char*; такое преобразование типов выполняются при помощи двух именованных функций — >c_str и >data.

• Все операторы сравнений, определенные для >std::string (например, >==, >!=, ><), перегружены для сравнения >const char* и >std::string в любом порядке (см. рекомендацию 29). Это позволяет избежать создания скрытых временных переменных.

Но и при этом возникают определенные неприятности, связанные с перегрузкой функций.

>void Display(int);

>void Display(std::string);


>Display(NULL); // вызов Display(int)

Этот результат для некоторых может оказаться сюрпризом. (Кстати, если бы выполнялся вызов >Display(std::string), код бы обладал неопределенным поведением, поскольку создание >std::string из нулевого указателя некорректно, но конструктор этого класса не обязан проверять передаваемое ему значение на равенство нулю.)

Ссылки

[Dewhurst03] §36-37 • [Lakos96] §9.3.1 • [Meyers96] §5 • [Murray93] §2.4 • [Sutter00] §6, §20, §39

41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур С)