10

I've been thinking why constexr and virtual are mutually exclusive, and someone added:

... constexpr is all about execution at compile time; if we're executing the function at compile time, we obviously know the type of data upon which it's acting at compile time as well, so late binding clearly isn't relevant.

However, it's possible that the dynamic type is not identical to the static type even in compile-time, and there might be cases where the dynamic type is needed:

class A {
public:
    /* virtual */ constexpr int foo() {
        return 1;
    }
};

class B : public A {
public:
    constexpr int foo() {
        return 2;
    }
};

constexpr int foo(A &a) {
    // The static type is fixed here.
    // What if we want to call B::foo() ?
    return a.foo();
}

int main() {
    B b;
    constexpr int c = foo(b);
    return 0;
}

That is, my question is

  • what's the (possible) rationale behind the standard prohibiting the combination of the two?
Dean Seo
  • 5,486
  • 3
  • 30
  • 49
  • *"constexpr is all about execution at compile time"* - That should say *"constexpr is all about __making__ execution at compile time __possible__"* – StoryTeller - Unslander Monica Nov 14 '17 at 05:48
  • @StoryTeller Not exactly; there are plenty of times that the compiler will execute code at compile time without `constexpr`. It's partially a hint that something should be done at compile time, but it also allows the *guarantee* in cases where you use the result in a context that requires a compile-time constant, like template parameters, array bounds, or the new `if constexpr`. – Daniel H Nov 14 '17 at 05:51
  • It's also worth noting that there's work being done to make "constexpr everything" into C++20. Even things like `new`. So it's possible we'll see some form of constexpr virtual functions as well. – StoryTeller - Unslander Monica Nov 14 '17 at 06:00
  • I don't think that's likely to make it into C++20, primarily because I don't think anybody's working on that in particular, but I wouldn't be surprised to see it in C++23. – Daniel H Nov 14 '17 at 06:10

3 Answers3

7

This restriction exists since constexpr was introduced in C++11:

10.1.5 The constexpr specifier [dcl.constexpr]

3 The definition of a constexpr function shall satisfy the following requirements:
(3.1) - it shall not be virtual;


But you're asking about rationale for this restriction, and not about the restriction itself.

The fact is, it may just be an oversight. Since in a constant expression the dynamic type of the object is required to be known, this restriction is unnecessary and artificial: This is what Peter Dimov and Vassil Vassilev assert in P1064R0, where they propose to remove it.

In fact, the wording for it no longer exists in the current draft.

Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
3

Because virtual calls are resolved via vtables/RTTI (RunTime Type Information) located in the memory layout of your object at runtime, the "true type" behind the "used object handle" is not known at compile time.

In your example:

constexpr int foo(A &a) {
    // The static type is fixed here.
    // What if we want to call B::foo() ?
    return a.foo();
}

If the foo member function is virtual there is no way for that foo function to be executed at compile time. And there is no point in marking a function as constexpr if it can never be executed at compile time. Hence there is no point in ever marking something both constexpr and virtual.


Now technically, depending on the code complexity, multiple cases might be resolvable by adding a completely new system (other than RTTI) to resolve virtual calls that would be compile time compatible (e.g some form of compiler meta data when everything is constexpr that remembers what type of instance you put in a pointer/reference) but that simply is not a thing right now.

If you want a runtime indirection (which is what virtual does) it doesn't make much sense to also want that to be executed at compile time.

P.S: sorry for the "compile time" and "runtime" spam.

Drax
  • 12,682
  • 7
  • 45
  • 85
  • I just thought to myself that (in theory) virtual calls CAN certainly be resolved in compile-time, too. I think this question is better off with not having a selected answer, since I guess it's a non-sense (to me) prohibiting this combination. I'll upvote your answer. Thank you. – eca2ed291a2f572f66f4a5fcf57511 Nov 14 '17 at 10:35
  • 1
    @il-seobbae actually, it seems to me that "foo() is a constant expression" implies " dynamic type is known at compile time", hence making constexpr virtual dispatch *always* possible; it would be interesting to prove it standard-wise or find some counterexamples ... – Massimiliano Janes Nov 14 '17 at 10:47
1

Indeed, it is possible for compilers to know at compile time what is the dynamic type of a constant expression. This is a possibility, that is some time used by optimizer to devirtualize calls at compile time.

But the c++ language evolution take also in consideration how difficult it is to implement it in compilers. If such a consideration were not taken, then the c++ standard will be too far from the c++ coded so that it would be unusefull.

Maybe, it has been evaluated that keeping trac of the static type of every reference during constant expression evaluation would be too expensive to implement. This is where reality hurts!

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • Just as an update (I know this answer is old), there's now a paper proposing the removal of this restriction. As a proof-of-concept, they implemented a [trivial patch](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html#_implementability) for Clang that enables this. Check my [answer](https://stackoverflow.com/a/51680949/3854787). – Not a real meerkat Aug 06 '18 at 16:25