C++17 之前的 virtual 和 constexpr

问题描述 投票:0回答:1

我对我读到的有关混合成员函数

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开始,

一些设计

是可能的) ).

c++ virtual constexpr-function
1个回答
5
投票

在 C++17 中,上述代码的格式不正确。您发现的是 GCC 中的编译器错误。标准对此非常明确:

constexpr函数的定义应满足以下要求:

不得是
    虚拟
  • [...]
-
[dcl.constexpr] p3

如果在类
constexpr

和直接或间接从

vf
派生的类
Base 
中声明虚拟成员函数
Derived
,则具有相同名称的成员函数
Base
,参数类型列表、 cv 限定符和 ref 限定符(或缺少相同的限定符)作为
vf
的声明,则
Base​::​vf
也是虚拟的(无论是否如此声明)并且它会覆盖
111 Derived​::​vf

-
[类.虚拟] p2

从这两节我们可以得出结论,

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

C++20 取消了对

constexpr

函数的这些限制,因此上述代码格式良好。

    

© www.soinside.com 2019 - 2024. All rights reserved.