1

Imagine next code that doesn't compile:

Try it online!

#include <type_traits>
#include <iostream>

int main() {
    struct A { int i = 123; };
    struct B { int j = 456; };
    B x;
    if constexpr(std::is_same_v<decltype(x), A>)
        std::cout << "A: " << x.i;
    else if constexpr(std::is_same_v<decltype(x), B>)
        std::cout << "B: " << x.j;
}

By this code I wanted to have several branches of code different for different types. In general I want different branches not only for certain type but for any constexpr condition.

Code above is uncompilable because looks like that compiler always tries to compile all if-constexpr branches even if they have compile time false condition.

Of cause I can solve task above by using template structs specialization like in compilable code below:

Try it online!

#include <type_traits>
#include <iostream>

template <size_t Id>
struct Code;

template <>
struct Code<0> {
    template <typename AT>
    void operator()(AT & x){
        std::cout << "A: " << x.i;
    }
};

template <>
struct Code<1> {
    template <typename BT>
    void operator()(BT & x){
        std::cout << "B: " << x.j;
    }
};

int main() {
    struct A { int i = 123; };
    struct B { int j = 456; };
    B x;
    Code<std::is_same_v<decltype(x), A> ? 0 : std::is_same_v<decltype(x), B> ? 1 : -1>()(x);
}

But the last solution has several drawbacks - 1) it is much more wordy, needs more lines of code. 2) it needs Code struct to be defined globally, because template structs can be defined only globally. 3) it needs all needed arguments for code to work to be passed/forwarded to operator() call.

constexpr variant looks much more elegant and is more intuitive to use. Is there any way to solve such task with if-constexpr? To force compiler not to compile compile-time-false-branches.

Arty
  • 14,883
  • 6
  • 36
  • 69
  • 4
    constexpr if only works in function templates and only when the condition depends on the template parameter(s). – NathanOliver Dec 30 '20 at 14:25
  • @mrks No, it doesn't answer my question. Basically from that answer I don't see how I can apply it to my case. Can you please transform [my first code](https://godbolt.org/z/d53Pqb) according to your linked answer, so that it works? Because if that not possible then my question should be re-opened. – Arty Dec 30 '20 at 14:32
  • Why not use preprocessor constructs à la [GNU autoconf](https://www.gnu.org/software/autoconf/) ? – Basile Starynkevitch Dec 30 '20 at 14:52
  • @Arty: Your code has no template parameters, so you cannot make your condition based on template parameters. So if the only way `constexpr if` can discard statements is to make the condition dependent on template parameters, that means you *cannot* do what you're trying to do with `constexpr if`. It's not a *macro*, and it's not `constexpr if`'s job to discard all statements. – Nicol Bolas Dec 30 '20 at 14:53

1 Answers1

1

As mentioned in the comments, "you have to make the discarded statement dependent of the template parameters" - What you are trying to do is not possible without introducing templates.

For example, this would "work" but its far from a good solution. I added it as answer as the similarity to the question that I marked as a duplicate was disputed.

#include <type_traits>
#include <iostream>

int main() {
    struct A { int i = 123; };
    struct B { int j = 456; };
    B x;
    
    [](auto const & x) {
        if constexpr(std::is_same_v<std::remove_cvref_t<decltype(x)>, A>) {
            std::cout << "A: " << x.i;
        } else if constexpr(std::is_same_v<std::remove_cvref_t<decltype(x)>, B>) {
            std::cout << "B: " << x.j;
        }
    }(x);
}
Arty
  • 14,883
  • 6
  • 36
  • 69
mrks
  • 8,033
  • 1
  • 33
  • 62
  • BTW, instead of `(const auto x)` probably better to use reference like `(auto const & x)`. – Arty Dec 30 '20 at 14:56
  • Thanks, very nice solution! It works for my code with extra use of `std::remove_cvref_t`. Accepting and upvoting it! – Arty Dec 30 '20 at 15:02