函数名称为什么不能与返回名称类型相同?

时间:2020-06-12 05:35:29

标签: c++ language-lawyer

不确定我缺少什么,但是为什么不编译呢?

class Game
{
};

class Actor
{
   Game* mGame;
   Game* Game() { return mGame; }
};

int main()
{
  Actor a();

  return 0;
}

g ++ -std = c ++ 17 main.cpp -o测试

main.cpp:8:10: error: declaration of ‘Game* Actor::Game()’ changes meaning of ‘Game’ [-fpermissive]
    8 |    Game* Game() { return mGame; }
      |          ^~~~
main.cpp:1:7: note: ‘Game’ declared here as ‘class Game’
    1 | class Game
      |      

很明显,如果我将功能更改为GetGame,就没有问题。只是想知道为什么-我想念什么?

谢谢!

3 个答案:

答案 0 :(得分:3)

由于已经添加了语言律师标签,并且对于是否由标准定义似乎有些困惑,因此这是该标准对现有答案的补充。

[basic.scope.hiding] 中:

如果在相同的声明区域(以任何顺序)中声明了具有相同名称的类名(11.2)或枚举名(9.6)以及变量,数据成员,函数或枚举器(不包括通过<可见的声明) em> using-directives (6.4.1)),则在可见变量,数据成员,函数或枚举器名称的任何位置隐藏类或枚举名称。

此外,在 [class.name] 中,我们发现:

如果在还声明了同名变量,函数或枚举器的范围内声明了类名,则当两个声明都在范围内时,只能使用修饰符引用该类-类型说明符(6.4.4)。 [示例:

struct stat {
  // ...
};

stat gstat;                // use plain stat to define variable

int stat(struct stat*);    // redeclare stat as function

void f() {
  struct stat* ps;         // struct prefix needed to name struct stat
  stat(ps);                // call stat()
}

-结束示例]

因此,在f的范围内,stat指的是该名称的功能,而struct stat(详细的类型说明符)是指该类。

或者在OP的示例中,在Actor的范围内,Game指代该名称的(成员)函数,而class Game(指称类型说明符)指代班级。

请注意,也可以使用::Game来引用该类。

最后,在 [class.name] 中,有一段关于编写此类代码的相关报价:

4 [注意:类名的声明在类定义或 Eraborated-type-specifier 中看到 identifier 后立即生效。 em>。例如,class A * A;首先将A指定为类的名称,然后将其重新定义为指向该类对象的指针的名称。这意味着必须使用详尽的形式class A来引用该类。这种带有名称的技巧可能会造成混淆,最好避免。 —尾注]

因此,标准不仅完全涵盖了这种情况-还建议您不要编写此类令人困惑的代码。

答案 1 :(得分:2)

请考虑以下内容:

class Game
{
};

class Actor
{
   Game* mGame;
   Game* Game() { return mGame; }
   void test() { auto g = Game(); /*constructing game or calling fn and saving ptr result?*/ }
};

可以,这可以使用::Game()this->Game()来解决,但是默认值是模棱两可的,因为标准没有说明在符号名重用的情况下首选哪种方法。因此,由于模棱两可而无法编译。

答案 2 :(得分:0)

使用您当前的代码,我在Compiler Explorer上使用各种编译器对其进行了测试...

这是3个不同编译器的结果,它们生成的程序集以及可能的错误或警告。所有编译器选项均设置为-std=c++17 -O3


x86-64铛声(trunk)

asm

main:                                   # @main
        xor     eax, eax
        ret

警告错误

warning: empty parentheses interpreted as a function
declaration [-Wvexing-parse]

突出显示a();作为相关警告


x86-64 gcc(树干)

无法编译!

警告错误

error: declaration of 'Game*' Actor::Game()' changes meaning of
'Game' [-fpermissive]

其中Game()突出显示为相关错误


x64 msvc v19.24

asm

main    PROC
        xor     eax, eax
        ret     0
main    ENDP

警告错误

warning C4930: 'Actor a(void)': prototyped function not called
(was a variable definition intended?)

突出显示Actor a();作为相关警告



这些是基于您当前代码的结果。现在,让我们看一下这3个相同的示例,只修改您的一小段代码...

我将把Actor a();函数中的main更改为Actor a{};,让我们看一下区别...



x86-64铛声(trunk)

asm

main:                                   # @main
        xor     eax, eax
        ret

警告错误

没有!


x86-64 gcc(树干)

无法编译!

警告错误

error: declaration of 'Game*' Actor::Game()' changes meaning of
'Game' [-fpermissive]

其中Game()突出显示为有问题的错误。


x64 msvc v19.24

asm

a$ = 0
main    PROC
$LN3:
        push    rdi
        sub     rsp, 16
        lea     rax, QWORD PTR a$[rsp]
        mov     rdi, rax
        xor     eax, eax
        mov     ecx, 8
        rep stosb
        xor     eax, eax
        add     rsp, 16
        pop     rdi
        ret     0
main    ENDP

警告错误

没有!



评估

如果将3进行比较,则会发现ClangMSVC都将在两种情况下都进行编译,但是,它们都将对第一种情况给出警告,而对第二种情况将进行编译而没有错误。 。另一方面,GCC在两种情况下均无法编译,并且生成相同的警告或编译器错误。

在第一种情况下,ClangMSVC可以确定您要调用所述类Actor的构造函数,但会生成警告消息,其中{ {1}}在使用GCC运算符时无法全部编译在一起。 ()无法确定您要呼叫GCC还是Actor::Game()

我们还可以推断Game::Game()Clang都从第一种情况向MSVC发出警告,而a()则将错误指向GCC在两种情况下。

要检查发生的情况,在第一种情况下,Game()Clang都生成几乎相同的程序集。在第二种情况下,MSVC仍在生成相同的程序集,但是Clang的足迹讲述了一个完全不同的故事!

根据我之间的比较,我可以根据目前的知识说些什么,而无需进行任何进一步的研究

MSVC

Actor a();

这是我从生成的警告,错误和汇编代码中看到的行为,这就是...

我相信在不同的编译器中可能是Actor a{};

我不能说这是否是Implementation Defined,但我可能会怀疑在某些情况下可以从上面生成的程序集,警告和错误中看到... >

如果将UB运算符用于该类的构造函数,则这三个编译器之间至少存在一些歧义。 ()甚至没有编译并生成错误。 GCCClang都在其中MSVC发出警告,将空括号解释为函数声明,而Clang发出警告,未调用原型函数的警告。

但是,当我们切换到MSVC运算符或初始值设定项列表... {}会生成与之前相同的程序集,但不再生成警告。 Clang给出了一套完整的MSVC集合,似乎两个类都在构造并且没有警告。 instructions在两种情况下都会出现错误,并说:“我放弃了您的意图”,这是我的错误消息,始终指向GCC



结论

所以回答您的问题:

为什么函数名不能与返回名类型相同?

取决于使用的上下文和所使用的编译器,它们可能是相同的,对于其他编译器,他们可能会或将拒绝它。

那么谁能说哪个编译器的解释是正确和正确的...我没有Game()的副本,所以我无法再深入探讨其中的standard部分,但是我可以概括一下正在发生的事情以及发生的原因,正如我刚才演示的那样。

我希望这种推理可以在命名约定中帮助您,以及编译器在尝试将此类名称和符号转换为对象时如何尝试解释此类名称和符号。

因此,在某些情况下可能存在歧义,而在其他情况下则没有歧义。这使我相信这是跨编译器实现的定义,因为language-lawyer该标准不需要特定的AFAIK符号,并且可能导致name-reuse!因此,这不是好习惯,绝对不是代码的味道!

所有这些都与硬件架构无​​关!