当定义成员函数时,哪些限定符必须出现在声明/定义/两者中

When defining a member function out-of-line, which qualifiers must appear in the declaration / definition / both?

本文关键字:定义 声明 函数 成员      更新时间:2023-10-16

我几乎可以肯定以前有人问过这个问题。不幸的是,我的C++已经变得如此生疏,以至于我甚至不知道该搜索什么。

有没有一条容易记住的经验法则可以告诉我,当我定义一个成员函数时,哪些限定符(inlinevirtualoverrideconstmutable等)必须出现(a)仅在声明中,(b)仅在定义中,(c)同时出现在声明和定义中?

示例:

struct Geometry {
    …
    virtual Geometry* clone() const = 0;
};
struct Point2 : public Geometry {
    …
    virtual Point2* clone() const override { … }
};

如果我想将Point2::clone定义为越界,试错会让我相信这将是正确的代码:

struct Point2 : public Geometry {
    …
    virtual Point2* clone() const override;
};
Point2* Point2::clone() const { … }
  • virtualoverride限定符在声明中只能出现
  • const必须同时出现在声明和定义中

我不想永远依靠试错。但我想明确限定符(即重复它们,即使它们是由基类隐含的。)有没有一个通用规则,哪个限定符必须精确到哪里,或者每个限定符的规则不同?

一般规则是,当删除限定符产生不同的函数重载时,该限定符必须同时出现在两个位置。所有其他限定符都保留在声明中。

必须同时出现在这两个位置的三个限定符是const和两种引用限定符,它们出现在C++11标准中:

void foo() const;
void foo() &;
void foo() &&;

所有其他限定符都保留在声明中。

了解哪些修饰符(或"说明符")必须在哪个实例中使用的最好方法是了解它们各自的含义和作用。

const是该方法"签名"的一部分。签名包括函数返回的内容、参数的类型以及它是否是常量方法(const部分)。这是不能改变的,必须保持原样

列表中的其他virtualoverride都与方法的声明有关。它不是方法签名的一部分,只能出现在方法声明中。

唯一的经验法则(如果有的话)是,当方法没有内联定义时,作为方法签名一部分的任何东西都必须保持不变。如果不是,它必须只是声明的一部分(但是,就像所有经验法则一样,总是有一个例外,inline关键字)。

请注意,默认参数值不被视为方法签名的一部分。只能在声明中指定默认参数值。但是,如果方法是内联定义的,那么默认参数值最终会成为方法定义的一部分!

我将采用另一种方法:我的经验法则:将它们放在两个位置,然后按照编译器的指示执行。它实现了标准规则,并将使您遵守这些规则。

任何经验法则的问题都是,你不能确定某个特定的例子是否正常,所以为什么不默认从一开始就检查呢。

如果你很少使用C++,那么学习一些无论如何都不能100%依赖的规则是没有意义的。如果你(开始)经常使用C++,那么在编译器多次告诉你该做什么之后,你就会自己得到要点。

因为这篇文章与其他文章的语气不一样,我会一直走无赖的路,用一个黑暗角落的例子给你这个非常不用的例子:显式实例化中的constexpr

template <class T>
constexpr auto foo(T)
{
}
template constexpr auto foo(int);
//       ^
// 6 : error: explicit instantiation shall not use 'constexpr' specifier

这不是你所问的,但它表明你可以将这种策略应用于更广泛的类似问题,否则你需要其他拇指规则