在编写一些C++代码(使用clang、x86_64-linux进行编译(时,我意外地编写了以下构造:
class Class {
*Class() {}
};
即构造函数名称前面带有星号(*(。
我注意到你可以在前面加任意数量的*;它也适用于析构函数,即
class Class {
********Class() {}
********~Class() {}
};
Clang编译它时没有任何错误或警告。
但是GCC发出警告
控制到达非无效功能的末尾
这让我相信我实际上是在声明一个返回类型为void*
(或void********
(的构造函数/析构函数。编写任何类型的带有值的返回语句都会产生错误(正如预期的那样(:
return nullptr;
return {};
...
有趣的是,生成的LLVM IR位代码正确地包含一个void函数:
define void @_ZN5ClassC2Ev(%struct.Class* %this) {...}
define void @_ZN5ClassD2Ev(%struct.Class* %this) {...}
搜索有关此的任何信息都没有找到任何结果。所以我的问题是:这个标准是符合C++还是GCC&Clang?或者可能是一些兼容性功能?如果正确,它的用例是什么。
它看起来像一个bug。因为它不符合标准。声明或定义构造函数的标准方法是在[class.cctor]/1:中
构造函数没有名称。在构造函数的声明中声明符是形式的函数声明符
ptr声明符(参数声明子句(noexcept说明符opt属性说明符seqptit其中ptr声明器仅由id表达式组成可选属性说明符seq和可选环绕括号,id表达式具有以下形式之一:
- 在属于类的成员规范但不是友元声明的成员声明中,id表达式是直接封闭类的注入类名
- 在属于类模板的成员规范但不是友元声明的成员声明中,id表达式为一个类名,用于命名立即封闭类模板;或
- 在命名空间范围的声明或友元声明中,id表达式是命名构造函数的限定id([class.qual](
类名不能是typedef名称。在构造函数中声明,可选decl说明符seq中的每个decl说明符应为friend、inline、explicit或constexpr。
正如您在我强调的部分中看到的,ptr-declarator
必须只是一个id表达式。或者用更简单、稍微不那么准确的术语来说,它必须只是类名。
那么,为什么可以在Clang和GCC中添加星号呢?他们似乎有一个bug,并且没有在这里对ptr-declarator
应用语义约束。这些约束很重要,因为纯语法确实允许使用星号。它在[dcl.dell]/4中(仅部分复制(:
声明器具有语法
ptr声明符:noptr声明器ptr运算符ptr声明符ptr运算符:*属性说明符seqoptcv限定符seqopt&属性说明符seqopt&属性说明符seq选项嵌套名称说明符*属性说明符seq-选择cv限定词seqopt
看到ptr-operator
位的语法了吗?它说*
可以出现在你放它的地方。或者&
甚至&&
。但我首先引用的声明中的语义使其成为非法。
因此,人们必须得出结论,这是GCC和Clang中的一个错误。