The following small example of C 20 code gives a segmentation fault when run. Why?
If I create an object of class Implementation and call the consteval function implementation.foo() it returns 42 as expected. However if I create a reference of type Interface and call the consteval function interface.foo() I get a segmentation fault. I am missing something in why this would happen.
// 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;
}
Link to Compiler Explorer
CodePudding user response:
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"; }
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";
}
GCC still chokes on this one and produces a segfault, but Clang produces the expected result. Again, I suspect a GCC bug.