如果我将代码拆分为 .h 和 .cpp 文件,则标头中使用的以下关键字中哪些也必须在 .cpp 文件中使用,并且不得进入 .cpp 文件中:

const, virtual, override, noexcept, constexpr

以及必须按什么顺序使用它们,即以下顺序是否正确:

virtual constexpr int foo() const override noexcept;

如果存在这样的规则,请随意给出。

7

  • 1
    constexpression不是关键字 – 您可能是指constexpr, whileoverride不是关键字。无论如何,(1) 按照惯例,.cpp 文件#include头文件,并且头文件可以(可选)#include彼此,并且 (2) 的效果#include是执行文本替换并将#include指令的内容替换为指定文件的内容。因此,出现在标头中的任何构造(包括任何关键字)都可以出现在源文件中,反之亦然。


    – 


  • 1
    它们位于哪个文件中都没有区别; “文件”在 C++ 中几乎是一个毫无意义的概念。区别在于类定义内部和外部的成员定义之间,并且在任何都有描述。


    – 


  • 2
    您的意思是问在声明之后提供定义时,函数或变量声明中使用的关键字是否必须在相同的定义中重复?


    – 

  • 1
    @tbxfreeware 是的,这正是我的意思。


    – 

  • 1
    必须重复和可以重复之间也有区别。我们中的一些人更喜欢virtual在所有虚拟函数上都有一个。即使不需要时。


    – 


2 个回答
2

“虚拟”说明符是类定义的一部分,并且仅属于类成员规范,在本例中是非静态类成员函数的声明(带或不带定义)。 “虚拟”成员是类的属性,而不是函数类型的属性。

“限定符”(const、易失性、、&&&constexpr/consteval说明符是函数的一部分,因此必须出现在(成员)函数的每个声明中(包括定义函数,无论是内联函数还是外联函数) :

// Class definition of X; specifies the members of X.
struct X : Base {
  virtual void f() const;  // virtual and override
  void g() && override;    // are part of "being a member"
};

// Function declarations and definitions:
// qualifiers are part of the function.
void X::f() const { /* ... */ }
void X::g() &&    { /* ... */ }

这与 .h 和 .cpp 文件本身无关,尽管人们通常会将外联定义放入 .cpp 文件中。 (如果将其保留在标头中,则当标头包含在不同的翻译单元中时,会导致重复定义,除非其中一个声明也显式指定为inline。相比之下,类成员函数的内联定义是隐式的inline。)

4

  • 非常感谢!


    – 

  • 很好的答案,感谢您向我指出裁判资格。直到!


    – 

  • 这份清单相当不完整。noexcept– 声明和定义之间的规范、语言链接、约束等也需要匹配。或者我猜一般来说,任何属于函数签名一部分的东西。


    – 


  • @JanSchultke是的,确实如此 – 这就是为什么我试图指出“类成员”和“函数类型”之间的根本区别。随着 C++ 的发展,细节肯定会发生变化。感谢您的补充。


    – 

简而言之,您需要重复属于其类型一部分的函数部分和/或使两个函数对应所必需的部分。因此,您需要重复:

  • 函数参数和返回类型
  • constvolatile预选赛
  • ref 限定符 (&&&)
  • requires条款
  • noexcept规格

通常,为了使标头中的函数声明与源文件中的定义匹配,这些声明需要。对于函数,这意味着它们需要(请参阅):

两个函数或函数模板声明声明,如果:

  • 声明函数,等效 ([temp.over.link]) 尾随的require-clause s(如果有,除非 [temp.friend] 中指定),并且,如果两者都是非静态成员,它们有,或者
  • 两者都声明具有相应签名和等效模板头以及尾随需求子句(如果有)的函数模板。

让我们只关注第一个项目符号,因为您不是在询问模板。首先,参数列表需要相同(不包括显式对象参数,即this auto和其他形式)。其次,对象参数需要相同,即constvolatile&&&必须匹配。第三,该requires条款需要等效。

noexcept规格也需要相同,因为noexcept适用于功能类型。不能有两个不同类型的对应声明:

int x(int);

int x(float);         // OK, x(float) does not correspond to x(int) and is a separate overload
int x(int) noexcept;  // error: x(int) declared with two different types

其他所有内容(例如属性virtual等)都适用于声明,并且不必在定义中重复。