8
votes

Found the following statement in Wiki:

C++11 introduced the concept of a constexpr-declared function; a function which could be executed at compile time. Their return values could be consumed by operations that require constant expressions, such as an integer template argument. However, C++11 constexpr functions could only contain a single expression that is returned (as well as static_asserts and a small number of other declarations).

C++14 relaxes these restrictions. Constexpr-declared functions may now contain the following: The conditional

  • ...
  • branching statements if and switch

So, Is it actually possible to have a switch in a constexpr function in c++14/c++17? And, if possible, what syntax is for that? For example, I'd like to have something like this:

enum class Terrain : std::uintmax_t {
    ROAD,
    SOIL,
    GRASS,
    MUD,
    SNOW,
};

constexpr float
getStepPrice(Terrain const& terrain)
{
    switch constexpr (terrain)
    {
        case Terrain::ROAD: return 1.0f;
        ...
    }
}
2
Have you tried compiling that? - Carcigenicate
I mean, this works: ideone.com/pjMrvr - Post Self
@Carcigenicate, Uh, oh. Turned out it just was a bug in my older g++ (4.9.2). But it works in clang 3.5 - user6071088
I learned nothing from this whole page. - Andrew

2 Answers

17
votes

So, Is it actually possible to have a switch in a constexpr function in c++14/c++17?

Yes.

And, if possible, what syntax is for that?

There is absolutely nothing special about the syntax, it's just a normal switch. Like this:

constexpr int fun (int i) {
    switch(i) {
        case 0: return 7;
        default: return 5;
    }
}

int main () {
    int arr[fun(3)];
}
24
votes

Not exactly. In the case of if constexpr, you can rest assured that the resulting code has no branching. Moreover, discarded statements need not compile. Those are the guarantees I think you would expect from a true switch constexpr.

class Dog;
class Snake;

#define USE_IF

template<typename Pet>
constexpr void foo(Pet pet) {
#ifdef USE_IF
    // This works
    if constexpr(std::is_same_v<Pet, Dog>)   pet.bark();
    else                                     pet.slither();
#else
    // This doesn't
    switch (std::is_same_v<Pet, Dog>) {
        case true:  pet.bark();    break;  // <== Error if Snake
        case false: pet.slither(); break;  // <== Error if Dog
    }
#else
}

BTW, I'm being a little nit-picky vis-a-vis Baum's accepted answer, which is fine, for practical puposes. I suspect you'd find good compilers will elide logically impossible bits from a switch-case statement with constexpr functions, as well as even non-constexpr inlined functions.