Если хотите, то можете представить себе, что C++ генерирует typedef для каждого имени тега:
typedef class string string;
К сожалению, это не точно соответствует действительности. Хотелось бы, чтобы все было так просто, но это не так. C++ не может генерировать такие typedef для struct, union или enum без введения несовместимости с языком C. Например, программа C декларирует и функцию, и структуру под одним и тем же именем status:
int status();
struct status;
Само собой, это плохая практика, и нельзя никому советовать так делать, но это C, так сделать можно. В этой программе status (сам по себе) относится к функции; struct status относится к типу.
Если C++ автоматически генерирует typedef-ы для тегов, то когда Вы скомпилируете эту программу на как C++, компилятор сгенерирует код:
typedef struct status status;
К сожалению, это имя будет конфликтовать с именем функции, и программа не скомпилируется. Вот почему C++ не может просто генерировать typedef для каждого тега.
В C++ действие тегов точно такое же, как и typedef-имен, за исключением того, что программа может объявить объект, функцию или энумератор с тем же именем и той же областью действия, как у тега. В этом случае объект, функция или энумератор скрывают имя тега. Программа может обратиться к имени тега только через использование ключевых слов class, struct, union или enum (какое из них подойдет) перед именем тега. Имя типа, состоящее их одного из этих ключевых слов, за которым идет тег, является конкретизированным спецификатором типа (elaborated-type-specifier [3]). Например, struct status и enum month как раз являются такими elaborated-type-specifier.
Таким образом, программа, которая содержит оба определения:
int status();
struct status;
становится такой же, когда компилируется как C++. Только имя status относится к функции. Программа может сослаться на тип status только при использовании к типу только при помощи конкретизированным спецификатором типа, т. е. struct status.
К каким ошибкам это может привести в программе? Вот пример кода:
// Листинг 1.
#include < iostream >
#include "lib.h"
using namespace std;
class foo
{
public:
foo();
operator char const *() const;
};
foo::foo()
{
}
foo::operator char const *() const
{
return "class";
}
int main()
{
char const *p;
p = foo();
cout << p << '\n';
return 0;
}
Здесь программа определяет класс foo с конструктором по умолчанию, и оператор конверсии преобразует объект foo в char const *. Выражение
в основном должно создать объект foo, и применить оператор конверсии. Следующий оператор вывода
должен отобразить класс foo, но этого не произойдет. Оператор отобразит функцию foo.
Этот неожиданный результат произошел потому, что программа подключила заголовок lib.h:
//Листинг 2.
inline
char const *foo()
{
return "function";
}
Этот заголовок определяет функцию, которая так же носит имя foo. Имя функции foo скрывает имя класса foo, так что обычное обращение к foo относится к функции, не к классу. Именно к классу можно обратиться только через elaborated-type-specifier:
Чтобы избежать подобного беспорядка в программе, нужно добавить следующий typedef для имени класса foo сразу перед или после определения класса:
Этот typedef приводит к конфликту между именем класса foo и именем функции foo (из библиотеки), и это вызовет ошибку при компиляции.
Применение typedef всегда требует от программиста дисциплины. Поскольку ошибки, подобные той что описана в листинге 1, встречаются довольно редко, то Вы вероятно никогда не сталкивались с подобной проблемой. Но если ошибка в Вашей программе может привести к серьезным последствиям, то Вы должны применить typedef независимо от малой вероятности возникновения ошибки.
Невозможно представить себе, зачем могло понадобиться скрыть имя класса именем функции или объекта, когда они находятся в одной области видимости. Так что скрывающие правила языка C были ошибкой, и они не должны быть расширены на классы в C++. Конечно же, Вы можете найти и исправить ошибку, но это требует дополнительной дисциплины в программировании и усилий, которых можно было бы избежать.
Комментарии
RSS лента комментариев этой записи