8

I'm trying to understand if constexpr fully.

I understand, that if if constexpr(expr) used in a template, and expr is dependent on a template parameter, then during instantiation, only one of the then/else branches will be instantiated, the other will be discarded.

I've got two questions:

  • Is it true, that if expr is not dependent on a template parameter, then no branches of if constexpr(expr) will be discarded? If yes, where does the standard say so? I don't see where the standard has the exception that discard happens only when expr is dependent.
  • Is if constexpr useful outside of templates? If yes, what are the use cases of this? Can you give some examples to understand its usefulness?
curiousguy
  • 8,038
  • 2
  • 40
  • 58
geza
  • 28,403
  • 6
  • 61
  • 135
  • I'd say yes, because it can replace feature selection constructions where you would normally have used the preprocessor's `#ifdef`. – Henri Menke Dec 02 '18 at 23:35
  • @HenriMenke: the "problem" is (as I see), that outside of templates, no branch will be discarded. So I don't really see the point of `if constexpr`. A simple `if` would do (almost) the same thing. – geza Dec 02 '18 at 23:40
  • Only MSVC discards the branch completely if it is not instantiated. https://godbolt.org/z/Zg1-PB – Henri Menke Dec 02 '18 at 23:58
  • 2
    @HenriMenke Even if it is never instantiated, the code in the branch still has to be syntactically valid. However, no diagnostic is required iirc, so gcc, clang, and MSVC are all correct here… – Michael Kenzel Dec 03 '18 at 00:55

1 Answers1

5

Is it true, that if expr is not dependent on a template parameter, then no branches of if constexpr(expr) will be discarded? If yes, where does the standard say so? […]

Yes, that is true. You're looking for [stmt.if]/2. Specifically this part:

[…] During the instantiation of an enclosing templated entity, if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated. […]

The best example I could find for a case where you would end up being value-dependent after instantiation is the one given by cppreference.com:

template<class T> void g() {
    auto lm = [](auto p) {
        if constexpr (sizeof(T) == 1 && sizeof p == 1) {
           // this condition remains value-dependent after instantiation of g<T>
        }
    };
}

Is if constexpr useful outside of templates? If yes, can you give some examples to understand its usefulness?

While all branches will be instantiated when the if constexpr does not appear inside of a template, [basic.def.odr]/10 still applies:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; […]

emphasis mine. That effectively means that an odr-use of an entity in a discarded statement doesn't count. For example:

void blub();

constexpr bool use_blub = false;

void f()
{
    if constexpr (use_blub)
    {
        blub();
    }
}

The call to blub() will not require that your program have a definition of blub() if the condition is false. Using a normal if, the program would still be required to provide a definition of blub() somewhere, even if it is never used. So you could, e.g., use if constexpr to toggle between calling some library function and calling some fallback implementation depending on whether the library is available (and being linked to). Apart from that, hypothetically, a compiler might not warn about unreachable code if it is unreachable due to an if constexpr like it potentially would with a normal if. I couldn't come up with an example of this using any actual compiler, however…

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • Thanks for the answer! I suspected that this is the part I need. However, I have a trouble to understand the part "the condition is not value-dependent after its instantiation". Can you explain what it means? – geza Dec 02 '18 at 23:42
  • I can see `if constexpr` being useful with _macros_, which are the other way of writing "generic" code – Mooing Duck Dec 02 '18 at 23:59
  • @geza I added an example for the "value-dependent after initialization" part. Basically, value-dependent means that the condition depends on a template parameter (which would typically be the case with an `if constexpr`). There are cases, e.g., with nested templates where the code containing the `if constexpr` would be instantiated during instantiation of the outer template but the condition cannot yet be evaluated because it still depends on an inner template parameter… – Michael Kenzel Dec 03 '18 at 02:32
  • Thanks! I'll leave the question open for a while, maybe someone can give a useful example of `if constexpr` in a non-value-dependent context. – geza Dec 03 '18 at 10:02
  • @MooingDuck: can you give an example of this? – geza Dec 03 '18 at 10:02
  • @geza I added such an example now. – Michael Kenzel Dec 03 '18 at 13:05