C++ 和 consteval 的分段错误

And*_*ers 6 c++ c++20

以下 C++20 代码的小示例在运行时出现分段错误。为什么?

如果我创建一个implementation类的对象并调用consteval函数implementation.foo(),它会按预期返回42。但是,如果我创建Interface类型的引用并调用 consteval 函数interface.foo(),则会出现分段错误。我遗漏了一些为什么会发生这种情况的信息。

// Compile with
//
// g++ -std=c++20 -Werror -Wall -Wextra -Wpedantic consteval.cpp -o consteval

#include <iostream>

struct Interface
{
        virtual consteval int foo(void) = 0;
};

struct Implementation final : public Interface
{
        consteval int foo(void) override { return 42; }
};

int main(void)
{
        Implementation implementation;

        std::cout << "implementation.foo() returns: " << implementation.foo() << std::endl;

        Interface& interface = implementation;

        std::cout << "interface.foo() returns: " << interface.foo() << std::endl;

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

链接到编译器资源管理器

HTN*_*TNW 3

Your code should not compile. I believe it is a bug in GCC that it tries to compile it and the weird segfault is just a consequence.

foo is consteval, so every usage of it must be part of a constant expression. But here you use it as interface.foo(), where interface is not a core constant expression (and foo is not static), so interface.foo() can't possibly be a constant expression. Thus your code is invalid and should simply fail with a compiler error (but should neither segfault nor "work" as it did in the comments; that's just GCC's fault).

If you correct the code to make the call valid, then you should get the right result. You could, say, wrap the thing in a constexpr function

consteval int example() {
   Implementation impl;
   Interface &intf = impl;
   return intf.foo();
}
int main() { std::cout << example() << "\n"; }
Run Code Online (Sandbox Code Playgroud)

This works fine on current GCC.

Another way to get to correct code is to make the Implementation object constexpr static and foo const-qualified:

struct Interface {
        virtual consteval int foo(void) const = 0; // constexpr objects are const, so need const-qual to call foo
};

struct Implementation final : public Interface {
        consteval int foo(void) const override { return 42; }
};
int main() {
    constexpr static Implementation impl; // need static to form constant expression references ("the address of impl is now constexpr")
    Interface const &intf = impl; // this reference is automatically usable in constant expressions (being of reference type and constant-initialized), note that it is to a *const* Interface, requiring the change to foo
    std::cout << intf.foo() << "\n";
}
Run Code Online (Sandbox Code Playgroud)

GCC still chokes on this one and produces a segfault, but Clang produces the expected result. Again, I suspect a GCC bug.