C++17 之前的 virtual 和 constexpr

Oer*_*ted 3 c++ virtual constexpr-function

virtual我对我读到的有关混合和constexpr成员函数的内容感到困惑。根据:

直到 C++17 为止,应该不可能混合constexprvirtual(标准在上面的链接中引用)

然而我设计了这个简单的例子:

#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();
}
Run Code Online (Sandbox Code Playgroud)

我正在设计一个具有大小属性​​的对象层次结构,可以通过GetSize我创建的成员函数检索该属性virtual。现在,可以使用编译时大小实例化具体类之一,并且我对其进行GetSizeoverride constexpr。然后我的玩具示例欺骗编译器在编译时不知道哪个对象将调用GetSize. 在Live中,我惊讶地发现 gcc 接受了该代码。clang 拒绝它,看起来与标准一致,而 msvc 与 clang 一样,但也声称该constexpr版本不能产生常量表达式,这对我来说似乎是不正确的。

因此,我的理解是所有编译器的行为都应该像 clang (但不是):这是正确的吗?在这种情况下,它接受这个构造是否是一个 gcc“错误”?附属问题:为什么 msvc 发出无法导致常量表达式错误?

除了学术兴趣之外,我的目的是知道我是否可以使用一些与动态多态性相关的编译时优化技术(直到 C++17,我知道,从 C++20 开始,某些设计是可能的)。

Jan*_*tke 5

C++17

\n

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

\n
\n

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

\n\n
\n

- [dcl.constexpr] p3

\n
\n

如果在类和直接或间接派生自 的类中vf声明虚拟成员函数,则具有与被声明,那么也是 virtual (无论是否如此声明)并且它覆盖111Base DerivedBasevfBase\xe2\x80\x8b::\xe2\x80\x8bvfDerived\xe2\x80\x8b::\xe2\x80\x8bvf Base\xe2\x80\x8b::\xe2\x80\x8bvf

\n
\n

- [类.虚拟] p2

\n

从这两节我们可以得出结论,constexpr virtual函数是不允许的,并且任何重写函数的函数virtual也不可以constexpr,因为它是隐式的virtual

\n

海湾合作委员会诊断

\n

有趣的是,我们没有得到GetSizebeing的诊断virtual constexpr,即使它是根据标准的虚函数。

\n
// implicitly virtual because of it overrides SizedObject::GetSize\nconstexpr size_t GetSize() const override final { return s; }\n
Run Code Online (Sandbox Code Playgroud)\n

如果我们冗余地标记该函数virtual,我们确实会得到诊断信息:

\n
constexpr virtual size_t GetSize() const override final { return s; }\n
Run Code Online (Sandbox Code Playgroud)\n
\n
<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]\n   14 |     constexpr virtual size_t GetSize() const override final { return s; }\n      |     ~~~~~~~~~ ^~~~~~~\n
Run Code Online (Sandbox Code Playgroud)\n
\n

MSVC 诊断

\n

至于:

\n
\n

附属问题:为什么 msvc 发出无法导致常量表达式错误?

\n
\n

诚然,这不是一个非常直观的错误消息。这就是 MSVC 在函数constexpr由于其签名而无法实现的所有情况下输出的内容。constexpr当设置函数的返回类型时,您会得到相同的消息std::vector<int>

\n

一般来说,constexpr意味着至少对于某些参数,函数必须能够产生常量表达式。如果函数签名使得这不可能,那么指定constexpr就是错误的。

\n

C++20

\n

C++20 取消了对constexpr virtual函数的这些限制,因此上面的代码将是格式良好的。

\n