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», а затем мысленно думать о каждой функции этого типа как о чем-то, что «обновляет адрес».
^ полностью согласен с этим. Если вы добавите тег language-lawyer, кто-то может сказать, соответствует ли это стандарту или нет, но я не хочу видеть обзор кода, который принимает это: P
Да, в этом примере я полностью согласен. Однако в моем реальном варианте использования есть несколько очень сложных прототипов функций и указателей функций, которые я использую, что делает определение типа функции полезным (мне нравится думать). В любом случае, цель моего вопроса больше в том, чтобы понять, почему он не компилируется, а не в стиле.
почему бы не использовать typedef только для репликации? разве этого не достаточно?
Точка данных: я использую Clang, и это позволяет переопределить Capitalized :: get_z.
fwiw вы можете добавить override в typedef, и он будет компилироваться, даже если нет ничего переопределенного. Еще один намек на то, что использовать typedef в этом месте - не лучшая идея;)
Компилируется с clang, похоже на ошибку gcc.
@ user463035818 Еще один баг. виртуальный спецификатор даже не допускается в синтаксисе для обычного объявления, только в член-декларация внутри класса.
@aschepler да похоже. Опять лязг правильно отклоняет
Подсказка: используйте std::toupper или std::tolower. Это не ответ на ваш вопрос, но он избавит вас от кода.
Мой реальный вариант использования не имеет ничего общего с заглавными буквами символов - он связан с загрузкой наборов данных в память, обновлением общих объектных файлов и множеством вещей, связанных с указателями функций. Я использовал заглавные буквы только для создания минимального примера для объяснения моего вопроса.
@CoryKramer, На самом деле, использование typedef для определения прототипов функций-членов - это настоящая идиома. C++ - довольно хрупкий язык, и это устройство помогает обеспечить единообразие прототипов функций-членов в нескольких объявлениях. В частности, до C++ 11 это устройство использовалось для устранения недостатка ключевого слова override.





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 Хорошее замечание. Думаю, есть и подобные случаи с неоднозначностью. Я думаю, что это может быть struct A { struct B {}; struct B override; };, который из-за того, что вы процитировали, объявляет нестатический член данных с именем override.
@ Rakete1111 Я не знаю, адресовано ли это мне или OP, но FWIW, я не. Я бы по крайней мере хотел проверить, может ли это уже быть исправлено на недавнем снимке GCC 9, прежде чем сообщать что-либо.
@hvd Направлено на вас. Ну, я могу воспроизвести с помощью clang и gcc trunk. Я пойду и доложу об этом.
@ Rakete1111 Я удивлен. В комментариях к вопросу говорится, что он работает должным образом на clang, и я не вижу в этом проблемы.
@hvd Хорошая мысль, но это не двусмысленно (последовательность пустых токенов не является допустимым декларатор, а только допустимым реферат-декларатор). Лучшим примером может быть struct A {}; struct B { friend A override; };, что не является законным, но сообщения об ошибках как GCC, так и Clang предполагают, что override является идентификатором здесь, например (Clang ++): "error: friends can only be classes or functions"
@ArneVogel Хороший момент, когда пустая последовательность токенов недействительна, и мне всегда нравится идея обманом заставить компилятор раскрыть больше информации через сообщения об ошибках.
Это действительно сбивающее с толку / плохое использование
typedef. Из-за этого внутри класса очень неясно, чтоget_z- это метод, а не переменная.