4

Consider the following code snippet:

#include <limits>
#include <stdexcept>

void g(unsigned) {
    // ...
}

template<typename UIntT>
void f(UIntT n)
{
    if constexpr (std::numeric_limits<UIntT>::max() > std::numeric_limits<unsigned>::max())
    {
        if (n > std::numeric_limits<unsigned>::max())
            throw std::length_error("Too long.");
    }

    g(n);
}

I wonder whether the 'if constexpr' clause is really useful here. Aren't compilers smart enough to find out whether the 'if' clause can ever be true for a given UIntT? If so, is this mandated by the standard?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
plexando
  • 1,151
  • 6
  • 22
  • 1
    In this case, it'd say most compilers would remove the complete `if`-block unless `::max() > ::max()` - at least when optimized - but you as a programmer want to make sure that's the case so in that sense, it makes sense to have it `constexpr`. If that compiles, you **know** that won't show up in the assemby for types where `::max() <= ::max()`. – Ted Lyngmo Sep 27 '19 at 17:32
  • Check out https://en.cppreference.com/w/cpp/language/static_assert which is less code – doug Sep 27 '19 at 18:44
  • @doug A run-time check involving non-constexpr `n` might still be needed here so how exactly is `static_assert` helpful then? – plexando Sep 27 '19 at 18:52
  • A runtime check doesn't involve n's value. Only it's type. So it works fine and gets removed if condition is valid. – doug Sep 27 '19 at 19:31
  • @doug Still not clear to me what you mean. Please write the respective lines of code using `static_assert`. – plexando Sep 27 '19 at 22:13
  • @plexando `constexpr if`'s do not exist in run-time. It must be evaluated while compiling. It's the whole point of it. – Ted Lyngmo Sep 27 '19 at 22:32
  • Sorry, you're right. I wasn't looking at the n in the conditional block. Your the n has to be checked if the condition is met so your code is fine, as are your assumptions about the conditional block removal. Good question – doug Sep 27 '19 at 22:36
  • @Ted Lyngmo I think there's a misunderstanding here. I know that constexpr if's are evaluated at compile time. That wasn't the point. It was the `static_assert`. Everything fine now. – plexando Sep 27 '19 at 22:40
  • @plexando I don't see any `static_assert` in the question. I clearly misunderstood something. – Ted Lyngmo Sep 28 '19 at 23:40

2 Answers2

4

Aren't compilers smart enough to find out whether the if clause can ever be true for a given UIntT?

Most are.

If so, is this mandated by the standard?

No, some optimizations have been given a name (RVO:s etc) and have later been incorporated into the language standard, but DEADC0DE optimizations aren't standardized (to my knowledge).

... but constexpr is

There's no way a conforming compiler would keep that block (if the condition is false) in your resulting binary - however you decide to optimize your code.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
1

This use of if constexpr has no observable difference from an if according to the C++ standard.

However, slightly different variants of it could result in an observable difference in what symbols a compilation unit uses. It seems plausible to me that would cause observable differences.

Most modern compilers can and will reduce that to if (false) during optimization even if not constexpr, and dead-branch elimination is a pretty simple optimization. In a debug build they might leave the dead code alone, while they might eliminate it with constexpr.

Compiler explorer is great to answer specific cases of this kind of question, as it makes it pretty easy to see the generated assembly of every major compiler. So if you want to know if there is a difference in a default MSVC 2015 debug or release setup, you can see it there.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • This is better than my naive take on it so you get my vote, but "while they might eliminate it with constexpr" sounds like there's a choice? Is there? – Ted Lyngmo Sep 27 '19 at 23:17
  • @tedl in a debug build, sure; dead code optimization is usually skipped in debug, it lets you move instruction pointer into dead branches. The `if constexpr` branch could be eliminated prior to optimization. So if you are massivdly memory constrained and don't want to optimize your debug builds, it could have a use. – Yakk - Adam Nevraumont Sep 28 '19 at 01:33
  • Would a debug build that "should" leave a `constexpr` block out, but didn't, be legal? I can see why one would want it but my interpretation is that there is no way that would happen in compliance with the standard. – Ted Lyngmo Sep 28 '19 at 03:35
  • @tedly What happens in unreachable assembly is not what the standard addresses. – Yakk - Adam Nevraumont Sep 28 '19 at 05:26
  • Doesn't the standard mandate that the `false` code block shout be vallidated (as correct code) and discarded? – Ted Lyngmo Sep 28 '19 at 09:30
  • @TedLyngmo The standard says nothing about what assembly is in the output binary, just the observable behavior of an execution where all operations are defined behavior. If you have further questions, please press the [Ask Question] button on this page, not [Add a Comment]; comments are not a good place to answer questions. – Yakk - Adam Nevraumont Sep 28 '19 at 15:11
  • Ok, I'll read up on it. I wrote my answer based on that idea so now I'm unsure if my answer is correct. It may still be correct even though I wrote it built on the wrong assumptions. Please vote it down if that's the case and I'll remove it so it doesn't give anyone the wrong ideas. – Ted Lyngmo Sep 28 '19 at 23:30