C ++ Для каких типов имя типа параметра может совпадать с именем параметра со спецификатором типа? - PullRequest
5 голосов
/ 23 мая 2011

Этот вопрос требует небольшого объяснения, извините.Я исправляю недосмотр в разборе doxygen, анализируя некоторый код C ++, и натолкнулся на необычный случай, который doxygen не учитывает.У меня есть исправление, но я хочу сделать его более общим, поэтому мне нужно некоторое объяснение.

Чтобы проиллюстрировать случай, когда doxygen терпит неудачу, я собираюсь определить надуманный пример с участием Симпсонов (потому что это кажется популярнымдля этих типов вопросов).Допустим, у нас есть следующее перечисление:

enum simpson { HOMER, MARGE, BART, LISA, MAGGIE };

Теперь мы хотим передать значение перечисления в метод (естественно, из класса Симпсонов), который выглядит следующим образом:

const char* voicedBy(simpson simpson)
{
    switch (simpson) {
        case HOMER:
            return "Dan Castellaneta";
        case MARGE:
            return "Julie Kavner";
        case BART:
            return "Nancy Cartwright";
        case LISA:
            return "Yeardley Smith";
        case MAGGIE:
            return "*suck* *suck*";
    }
}

К сожалению, это приводит к ошибке компилятора, потому что тип перечисления 'simpson' не может совпадать с именем параметра 'simpson' (в отличие, скажем, в C #).Но у C ++ есть ответ на это.Вы помещаете ключевое слово enum перед именем типа следующим образом:

const char* voicedBy(enum simpson simpson)

, и код теперь будет компилироваться и выполняться.К сожалению, doxygen не учитывает этот случай, и поэтому он обрабатывает всю строку «enum simpson simpson» как тип параметра без имени параметра.Я придумал некоторый код для исправления doxygen в случае перечисления, как указано выше.

Мой вопрос: для каких других типов подходит этот вид трюка?struct ?, union ?, typedef ?, другие?И если уж на то пошло, у понятия «спецификатор типа для параметра метода с тем же именем, что и у имени параметра» есть имя, чтобы я мог получить более подробную информацию о нем?

Ответы [ 4 ]

3 голосов
/ 23 мая 2011

В C каноническое имя struct, union или enum включает этот префикс:

struct Point {
    int x, y;
};

enum Type {
    FIRST, SECOND, THIRD
};

struct Point p;
enum Type t;

, который является источником идиомы создания typedef именис удаленным префиксом:

typedef struct Point {
    int x, y;
} Point;

typedef enum Type {
    FIRST, SECOND, THIRD
} Type;

struct Point p;
enum Type t;
Point p;
Type t;

C ++ также имеет это, с тем же поведением, что и для class, и аналогичным поведением для template и typename в шаблонах.Однако это также устраняет требование включения префикса, за исключением неоднозначных случаев.

Я не думал, что у этой концепции есть имя, но я исправляюсь: это разработанный спецификатор типа .Подходящим обходным путем для этого может быть размещение комментариев Doxygen в декларации, а не в определении.

2 голосов
/ 23 мая 2011

Какой компилятор и версию вы используете?

void foo( simpson simpson ) {}

Нет enum присутствует, то есть вам не нужно использовать разработанный спецификатор типа в этом контексте, и он прекрасно компилируется в gcc 4.2 и 4.6. Проблема в том, что внутри функции, имя аргумента скрывает * типа, и если вы хотите объявить новую переменную с этим типом внутри , которая охватывает вас потребуется подробный спецификатор типа, но в сигнатуре функции он анализируется слева направо, и это означает, что первым simpson является enum и в это время нет конфликта. Второй simpson вводит локальное имя, и с этого момента simpson относится к параметру, а не к типу.

void relationship( /*enum*/ simpson simpson, enum simpson other = HOMER );
//                   ^^^^ optional           ^^^^ required
{
   enum simpson yet_another = simpson;
// ^^^^ required              ^^^^^^^ first argument!
}

Имя того же типа скрытие может произойти, если вы определите функцию с тем же именем, что и тип, который вы хотите:

void simpson();
void voicedBy( enum simpson s );
//             ^^^^ required

Обратите внимание, что если вы добавляете typedef, то в этом последнем случае все немного меняется: между именем typedef-ed и именем функции (или именем переменной в той же области видимости) будет конфликт имен.

* Здесь скрывает не используется в смысле переменной в одной области видимости, скрывающей переменную с тем же именем во внешней области видимости. В C ++, как и в C, есть два пространства идентификаторов, одно для пользовательских типов, а другое для большей части всего остального, включая псевдонимы типа typedef. Поиск в C ++ выполняется из внутренней области во внешнюю область, и в каждой области выполняется поиск в глобальном пространстве идентификаторов, если идентификатор не найден, то в пространстве идентификаторов пользовательских типов выполняется поиск того же идентификатора. Этот последний шаг не выполняется в C, где всегда требуются подробные спецификаторы типов. Только если это также не сработает, компилятор перейдет к следующей области видимости.

1 голос
/ 23 мая 2011

То, что вы сделали, - это то же самое, что и кодеры C, которые делают весь день - префикс своих пользовательских типов с соответствующим ключевым словом. То же самое работает для struct, class, union, в typedefs, объявлениях переменных, где угодно в основном.

1 голос
/ 23 мая 2011

struct / class / union также.В Стандарте "подробный спецификатор типа", состоящий из "идентификатора ключа класса", см. 3.4.4-1.(Кстати - если switch case return с, то нет необходимости break.)

...