Использование override для функции, которая была объявлена ​​с помощью typedef

C++ 11 представил спецификатор override для функций, и я считаю его полезным, поскольку он явно указывает на то, что виртуальная функция переопределяется. Однако я не могу заставить его работать для функции, которая была объявлена ​​с использованием typedef.

Я понимаю, что «переопределение» - это не ключевое слово, оно как-то связано с этим?

Следующий код иллюстрирует мою точку зрения:

#include <iostream>

typedef char ReturnsChar();

class Basic
{
    public:
    virtual char get_a();
    virtual ReturnsChar get_z;
};

char Basic::get_a() { return 'a'; }
char Basic::get_z() { return 'z'; }

class Capitalized : public Basic
{
    public:
    // Can override explicitly if I use the normal definition
    char get_a() override;

    // Compiles if I use the typedef but not 'override'
    ReturnsChar get_z;

    // Will not compile, but would like to do this
    //ReturnsChar get_z override; 

};

char Capitalized::get_a() { return 'A'; }
char Capitalized::get_z() { return 'Z'; }

int main()
{
    Basic foo;
    Capitalized bar;

    std::cout << foo.get_a() << std::endl; // a
    std::cout << foo.get_z() << std::endl; // z
    std::cout << bar.get_a() << std::endl; // A
    std::cout << bar.get_z() << std::endl; // Z
}

Я использую GNU g ++ 8.2.0, и ошибка, которую он мне дает,

error: expected ';' at end of member declaration
ReturnsChar get_z override;
            ^~~~~
                      ;
error: ‘override’ does not name a type; did you mean ‘ctermid’?
     ReturnsChar get_z override;
                       ^~~~~~~~
                       ctermid

Обновлено: Что касается комментариев, я понимаю, что этот стиль неясен. Меня больше интересует, почему это не компилируется и что именно делает «переопределение» (особенно потому, что это не ключевое слово). Кстати, мне кажется, что в некоторых случаях функции определения типа могут быть понятны, например:

void (*foo(int x, void (*f)(int)))(int);

Это трудно читать, особенно если всплывает часто. Я могу просто ввести это как «UpdateAddressFunction», а затем мысленно думать о каждой функции этого типа как о чем-то, что «обновляет адрес».

Это действительно сбивающее с толку / плохое использование typedef. Из-за этого внутри класса очень неясно, что get_z - это метод, а не переменная.

Cory Kramer 31.08.2018 13:27

^ полностью согласен с этим. Если вы добавите тег language-lawyer, кто-то может сказать, соответствует ли это стандарту или нет, но я не хочу видеть обзор кода, который принимает это: P

463035818_is_not_a_number 31.08.2018 13:31

Да, в этом примере я полностью согласен. Однако в моем реальном варианте использования есть несколько очень сложных прототипов функций и указателей функций, которые я использую, что делает определение типа функции полезным (мне нравится думать). В любом случае, цель моего вопроса больше в том, чтобы понять, почему он не компилируется, а не в стиле.

Globe Theatre 31.08.2018 13:31

почему бы не использовать typedef только для репликации? разве этого не достаточно?

463035818_is_not_a_number 31.08.2018 13:32

Точка данных: я использую Clang, и это позволяет переопределить Capitalized :: get_z.

Eljay 31.08.2018 13:37

fwiw вы можете добавить override в typedef, и он будет компилироваться, даже если нет ничего переопределенного. Еще один намек на то, что использовать typedef в этом месте - не лучшая идея;)

463035818_is_not_a_number 31.08.2018 13:38

Компилируется с clang, похоже на ошибку gcc.

n. 1.8e9-where's-my-share m. 31.08.2018 13:40

@ user463035818 Еще один баг. виртуальный спецификатор даже не допускается в синтаксисе для обычного объявления, только в член-декларация внутри класса.

aschepler 31.08.2018 13:45

@aschepler да похоже. Опять лязг правильно отклоняет

463035818_is_not_a_number 31.08.2018 13:47

Подсказка: используйте std::toupper или std::tolower. Это не ответ на ваш вопрос, но он избавит вас от кода.

Thomas Matthews 31.08.2018 16:38

Мой реальный вариант использования не имеет ничего общего с заглавными буквами символов - он связан с загрузкой наборов данных в память, обновлением общих объектных файлов и множеством вещей, связанных с указателями функций. Я использовал заглавные буквы только для создания минимального примера для объяснения моего вопроса.

Globe Theatre 31.08.2018 17:05

@CoryKramer, На самом деле, использование typedef для определения прототипов функций-членов - это настоящая идиома. C++ - довольно хрупкий язык, и это устройство помогает обеспечить единообразие прототипов функций-членов в нескольких объявлениях. В частности, до C++ 11 это устройство использовалось для устранения недостатка ключевого слова override.

ach 31.08.2018 18:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
16
12
948
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

I understand that 'override' is not a keyword, has it got something to do with that?

Это действительно так: override распознается только в нескольких конкретных контекстах и ​​доступен как имя для обычного использования в противном случае. Однако я считаю, что это ошибка компилятора, и это тот контекст, в котором компилятор должен распознает ее в своем особом значении.

Соответствующая грамматическая продукция для этого:

[class.mem]

member-declarator:
  declarator virt-specifier-seqopt pure-specifieropt

Это не требует, чтобы Вирт-спецификатор-последовательность появлялся только в объявлении, содержащем параметры. Единственное подобное требование изложено ниже:

[class.mem]p8:

A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).

В вашем случае объявление является одной из виртуальных функций-членов. Я считаю, что здесь следует принять override.

Единственная загвоздка - [lex.name] / 2 «Если не указано иное, любая двусмысленность относительно того, имеет ли данный идентификатор особое значение, разрешается для интерпретации токена как обычного идентификатор». Я не вижу способа, чтобы этот код мог быть двусмысленным, но я не уверен на 100%.

aschepler 31.08.2018 13:39

Спасибо, это очень ясно. Я очень рад, что обнаружил настоящую ошибку компилятора, которая стоит пометить список желаний!

Globe Theatre 31.08.2018 13:45

@aschepler Хорошее замечание. Думаю, есть и подобные случаи с неоднозначностью. Я думаю, что это может быть struct A { struct B {}; struct B override; };, который из-за того, что вы процитировали, объявляет нестатический член данных с именем override.

user743382 31.08.2018 13:45

@ Rakete1111 Я не знаю, адресовано ли это мне или OP, но FWIW, я не. Я бы по крайней мере хотел проверить, может ли это уже быть исправлено на недавнем снимке GCC 9, прежде чем сообщать что-либо.

user743382 31.08.2018 19:47

@hvd Направлено на вас. Ну, я могу воспроизвести с помощью clang и gcc trunk. Я пойду и доложу об этом.

Rakete1111 31.08.2018 19:53

@ Rakete1111 Я удивлен. В комментариях к вопросу говорится, что он работает должным образом на clang, и я не вижу в этом проблемы.

user743382 31.08.2018 20:00

@hvd Хорошая мысль, но это не двусмысленно (последовательность пустых токенов не является допустимым декларатор, а только допустимым реферат-декларатор). Лучшим примером может быть struct A {}; struct B { friend A override; };, что не является законным, но сообщения об ошибках как GCC, так и Clang предполагают, что override является идентификатором здесь, например (Clang ++): "error: friends can only be classes or functions"

Arne Vogel 31.08.2018 23:11

@ArneVogel Хороший момент, когда пустая последовательность токенов недействительна, и мне всегда нравится идея обманом заставить компилятор раскрыть больше информации через сообщения об ошибках.

user743382 01.09.2018 00:22

Другие вопросы по теме