77

Inspired by this answer, I tried to copy and paste (and add testing in main()) this code:

template<typename T>
std::tuple<int, double> foo(T a) {
    if constexpr (std::is_same_v<int, T>)
        return {a, 0.0};
    else if (std::is_same_v<double, T>)
        return {0, a};
    else
        return {0, 0.0};
}

int main() {
    auto [x, y] = foo("");
    std::cout << x << " " << y;
}

This is very straightforward - if T is deduced as int, we want to return a tuple of [a, 0.0]. If T is deduced as double, we want to return a tuple of [0, a]. Otherwise, we want to return [0, 0.0].

As you can see, in the main() function, I am calling foo with const char* argument, which should result in x and y being 0. That is not the case.

While trying to compile it, I encountered a strange error:

error: could not convert '{0, a}' from '<brace-enclosed initializer list>' to 'std::tuple<int, double>'

And I was like what?. Why on earth would I want that... I specifically used std::is_same to enable return {0, a} only when the type of a is deduced as double.

So I quickly ran to cppreference on if-constexpr. At the bottom of the page, above Notes, we can see this snippet of code:

extern int x; // no definition of x required
int f() {
if constexpr (true)
    return 0;
else if (x)
    return x;
else
    return -x;
}

I thought to myself oookay..? I can't really see what's wrong with the original code. They use the same syntax and semantics....

But I was curious. I was curious if maybe something odd (at that time) might fix that issue, so I changed the original code to:

template<typename T>
std::tuple<int, double> foo(T a) {
    if constexpr (std::is_same_v<int, T>)
        return {a, 0.0};
    else if constexpr (std::is_same_v<double, T>) // notice the additional constexpr here
        return {0, a};
    else
        return {0, 0.0};
}

int main() {
    auto [x, y] = foo("");
    std::cout << x << " " << y;
}

And voilà! The code compiled and executed as expected. So, my question is - Do we need to put constexpr after every if statement in if-else statement in these kind of situations? Or is it just my compiler? I am using GCC 7.3.

Fureeish
  • 12,533
  • 4
  • 32
  • 62

1 Answers1

101

Do we need to put constexpr after every if statement in if-else block in these kind of situations?

Yes. The else-if block1 is a lie :), there are only if blocks1 and else blocks1. This is how your code is seen by the compiler:

if constexpr (std::is_same_v<int, T>)
    return {a, 0.0};
else // {
    if (std::is_same_v<double, T>)
        return {0, a};
    else
        return {0, 0.0};
// }

else if (/*...*/) is just a formatting convention that everyone uses. As such, you can clearly see that the second constexpr is needed.


1: "block" is not the correct terminology. if is a statement (with optional else part). A block is { /*...*/ }.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • 8
    @Fureeish Also note the cppref's example is correct, the missing `constexpr` is there by design to show that you don't have to have a definition of `x` because the else block (see answer) is discarded. – Rakete1111 Sep 16 '18 at 17:04
  • Question: Defining function `foo` as `constexpr` will make everything inside a `constexpr` or still all `if`s should be decorated with `constexpr`? – Marek R Sep 16 '18 at 17:20
  • 1
    @MarekR The inner `if`'s still need to be decorated. Just because `foo` is `constexpr` doesn't mean that everything inside the function is `constexpr` (as you can still call the function at runtime). – Rakete1111 Sep 16 '18 at 17:21
  • 1
    There are no blocks (`{}`) in the original example, though, except for the ones required for the function definitions. I recommend saying "statements", in keeping with the standard terms. – Arne Vogel Sep 17 '18 at 10:40
  • 1
    @ArneVogel Yeah, I just used the same terminology as the OP. Will change – Rakete1111 Sep 17 '18 at 10:42
  • @ArneVogel and Rakete111, I also edited the original question to match your comment – Fureeish Sep 17 '18 at 11:56
  • 1
    Worth noting that syntactically, this interpretation of `if else` is no different than omitting the `{ }` on another statement like `if(condition) return 0;` – JAD Sep 17 '18 at 15:45
  • 2
    `else` is not even a statement. It's half of one. – T.C. Sep 17 '18 at 23:26
  • 1
    @T.C. So... it's a state? – Barry Oct 10 '19 at 19:18
  • 1
    @Barry No, it's an ent. – T.C. Oct 10 '19 at 19:22