我对我读到的有关混合成员函数
virtual
和 constexpr
的内容感到困惑。
根据:
直到包含 C++17,不应该可以混合
constexpr
和 virtual
(标准在上面的链接中引用)
然而我设计了这个简单的例子:
#include <cstddef>
struct SizedObject {
virtual size_t GetSize() const = 0;
};
struct DynSizedObject : public SizedObject {
size_t s;
size_t GetSize() const override final { return s; }
};
struct StatSizedObject : public SizedObject {
const size_t s;
constexpr size_t GetSize() const override final { return s; }
constexpr explicit StatSizedObject(const size_t i) : s(i) {}
};
int main(int argc, char** argv) {
constexpr StatSizedObject SS(42);
DynSizedObject DS;
DS.s = argc + 2;
SizedObject const * p;
if (argc > 3) {
p = &SS;
} else {
p = &DS;
}
return p->GetSize();
}
我正在设计一个具有 size 属性的对象层次结构,可以通过我创建的
GetSize
成员函数检索该属性。现在,可以使用编译时大小实例化一个具体类,并且我将其 virtual
覆盖 GetSize
。然后我的玩具示例欺骗编译器在编译时不知道哪个对象将调用 constexpr
。在 Live中,我惊讶地发现 gcc 接受了代码。 clang 拒绝它,看起来与标准一致,而 msvc 与 clang 一样,但也声称
GetSize
版本不能产生常量表达式,这对我来说似乎是不正确的。 因此我的理解是所有编译器都应该表现得像 clang (但不是):这是正确的吗?在这种情况下,它接受这个构造是否是一个 gcc“错误”?附属问题:为什么 msvc 会发出 cannot result in a Constant expression
错误? 除了学术兴趣之外,我的目的是知道我是否可以使用一些与动态多态性相关的编译时优化技术(直到C++17,我知道,从C++20开始,
一些设计constexpr函数的定义应满足以下要求:
[dcl.constexpr] p3如果在类不得是-
[类.虚拟] p2constexpr
和直接或间接从
派生的类vf
中声明虚拟成员函数Base
,则具有相同名称的成员函数Derived
,参数类型列表、 cv 限定符和 ref 限定符(或缺少相同的限定符)作为Base
的声明,则vf
也是虚拟的(无论是否如此声明)并且它会覆盖 111
Base::vf
Derived::vf
。 -
Base::vf
函数是不允许的,并且任何覆盖
constexpr virtual
函数的函数也不能是virtual
,因为它是隐式的constexpr
。海湾合作委员会诊断
virtual
为
GetSize
的诊断,即使根据标准它是一个虚函数。virtual constexpr
如果我们冗余地标记函数
// implicitly virtual because of it overrides SizedObject::GetSize
constexpr size_t GetSize() const override final { return s; }
,我们确实会得到诊断信息:
virtual
constexpr virtual size_t GetSize() const override final { return s; }
MSVC 诊断
附属问题:为什么 msvc 发出无法导致常量表达式错误?
不可否认,这不是一个非常直观的错误消息。这就是 MSVC 在所有情况下输出的结果,其中函数由于其签名而不能是
<source>:14:15: warning: member 'GetSize' can be declared both 'virtual' and > 'constexpr' only in '-std=c++20' or '-std=gnu++20' [-Wc++20-extensions]
14 | constexpr virtual size_t GetSize() const override final { return s; }
| ~~~~~~~~~ ^~~~~~~
。当设置
constexpr
函数 constexpr
的返回类型时,您会得到相同的消息。一般来说,std::vector<int>
意味着至少对于某些参数,函数必须能够产生常量表达式。如果函数签名使得这不可能,那么指定
constexpr
是错误的。C++20
constexpr
函数的这些限制,因此上述代码格式良好。