Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"constexpr if" vs "if" with optimizations - why is "constexpr" needed?

C++1z will introduce "constexpr if" - an if that will have one of branches removed, based on the condition. Seems reasonable and useful.

However, is it not possible to do without constexpr keyword? I think that during compilation, compiler should know wheter condition is known during compilation time or not. If it is, even the most basic optimization level should remove unnecessary branch.

For example (see in godbolt: https://godbolt.org/g/IpY5y5):

int test() {     const bool condition = true;     if (condition) {       return 0;     } else {       // optimized out even without "constexpr if"       return 1;     } } 

Godbolt explorer shows, that even gcc-4.4.7 with -O0 did not compile "return 1", so it achieved what was promised with constexpr if. Obviously such old compiler will not be able to do so when condition is result of constexpr function, but fact remains: modern compiler knows whether condition is constexpr or not and doesn't need me to tell it explicitly.

So the question is:

Why is "constexpr" needed in "constexpr if"?

like image 361
MateuszL Avatar asked Dec 05 '16 10:12

MateuszL


People also ask

What does if constexpr do?

Constexpr ifIf the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

Should I use constexpr everywhere?

Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.

What is if constexpr in C++?

This is a big one! The static-if for C++! The feature allows you to discard branches of an if statement at compile-time based on a constant expression condition.

Which is false about constexpr?

Short answer: static_assert(false) should never appear in a constexpr if expression, regardless of whether it's in a template function or whether it's in the discarded branch.


1 Answers

This is easy to explain through an example. Consider

struct Cat { void meow() { } }; struct Dog { void bark() { } }; 

and

template <typename T> void pet(T x) {     if(std::is_same<T, Cat>{}){ x.meow(); }     else if(std::is_same<T, Dog>{}){ x.bark(); } } 

Invoking

pet(Cat{}); pet(Dog{}); 

will trigger a compilation error (wandbox example), because both branches of the if statement have to be well-formed.

prog.cc:10:40: error: no member named 'bark' in 'Cat'     else if(std::is_same<T, Dog>{}){ x.bark(); }                                      ~ ^ prog.cc:15:5: note: in instantiation of function template specialization 'pet<Cat>' requested here     pet(Cat{});     ^ prog.cc:9:35: error: no member named 'meow' in 'Dog'     if(std::is_same<T, Cat>{}){ x.meow(); }                                 ~ ^ prog.cc:16:5: note: in instantiation of function template specialization 'pet<Dog>' requested here     pet(Dog{});     ^ 

Changing pet to use if constexpr

template <typename T> void pet(T x) {     if constexpr(std::is_same<T, Cat>{}){ x.meow(); }     else if constexpr(std::is_same<T, Dog>{}){ x.bark(); } } 

only requires the branches to be parseable - only the branch that matches the condition needs to be well-formed (wandbox example).

The snippet

pet(Cat{}); pet(Dog{}); 

will compile and work as expected.

like image 122
Vittorio Romeo Avatar answered Oct 10 '22 16:10

Vittorio Romeo