Search code examples
c++constexprcompile-timeconditional-compilationif-constexpr

How to skip uncompilable code through constexpr-if in C++?


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.


Solution

  • 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);
    }