使用“override”关键字来重写一个已经使用“typedef”声明的函数

16

C++11引入了“override”关键字用于函数,我觉得很有用,因为它明确表示虚函数正在被重写。但是,对于使用typedef声明的函数,似乎无法让它起作用。

我知道“override”不是一个关键字,这与它有关吗?

以下代码说明了我的观点:

#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

编辑:针对评论,我知道这种风格不清晰。我更关心的是为什么这个代码无法编译,以及'override'到底做了什么(特别是它不是一个关键字)。此外,我觉得在某些情况下,为函数定义typedef可能会更清晰,比如:

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

那很难读,尤其是经常出现。我可以把它typedef为“UpdateAddressFunction”,然后在脑海中将该类型的每个函数视为“更新地址”的内容。


9
这真的是一个很混乱/糟糕的typedef使用。这使得在类内不清楚get_z是一个方法而不是变量。 - Cory Kramer
3
完全同意这一点。如果您添加了“律师语言”标签,有人可能会告诉您是否符合标准,但我不想看到接受此代码审查的情况:P - 463035818_is_not_a_number
1
是的,在这个例子中我完全同意。然而,在我的实际使用情况中,有一些非常复杂的函数原型和函数指针,我使用typedef来定义函数是很有用的(我喜欢这样认为)。无论如何,我提出问题的目的更多是为了理解为什么它不能编译,而不是风格。 - Globe Theatre
1
为什么不仅仅typedef返回类型?那不就足够了吗? - 463035818_is_not_a_number
1
@user463035818 又发现一个 bug。virt-specifier 甚至不允许在常规声明的语法中出现,只能在类内 member-declaration 中使用。 - aschepler
显示剩余7条评论
1个回答

23
我理解'override'不是一个关键字,这与什么有关吗?
确实有关系:override只在一些特定的上下文中被识别,并且在其他情况下可用作普通名称。然而,我认为这是编译器的错误,这是编译器应该在其特殊含义中认识它的上下文之一。
相关的语法结构如下: member-declarator:  declarator virt-specifier-seq pure-specifier 这并不要求只出现在包含参数的声明中。唯一类似的要求如下: p8: 只能出现在虚拟成员函数(10.3)的声明中。
在您的情况下,声明是虚拟成员函数之一。我相信override应该在这里被接受。

唯一的限制是[lex.name]/2“除非另有规定,否则对于给定标识符是否具有特殊含义的任何歧义都将解决为将令牌解释为常规标识符。”我没有看到代码可能存在歧义的方式,但我不确定百分之百。 - aschepler
谢谢,非常清晰。我很高兴发现了一个实际的编译器错误,这是一个可以勾掉任务清单上的项目! - Globe Theatre
@aschepler 很好的观点。我认为有一些类似的模糊情况。我认为 struct A { struct B {}; struct B override; }; 可能是其中之一,由于你引用的东西,它声明了一个名为 override 的非静态数据成员。 - user743382
@Rakete1111 我不知道这是针对我还是针对OP的,但是就我而言,我还没有。在报告任何问题之前,我至少想检查一下最近的GCC 9快照是否已经修复了这个问题。 - user743382
@ArneVogel 很好的观点,空令牌序列是无效的,我总是喜欢通过编译器的错误消息来揭示更多信息的想法。 - user743382
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接