Как пропустить некомпилируемый код через constexpr-if в C++?

Представьте себе следующий код, который не компилируется:

Попробуйте онлайн!

#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;
}

По этому коду я хотел иметь несколько разных веток кода для разных типов. В общем, мне нужны разные ветки не только для определенного типа, но и для любого условия constexpr.

Код выше не компилируется, потому что похоже, что этот компилятор всегда пытается скомпилировать все ветки if-constexpr, даже если они имеют ложное условие времени компиляции.

Конечно, я могу решить задачу выше, используя специализацию шаблонных структур, как в компилируемом коде ниже:

Попробуйте онлайн!

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

Но у последнего решения есть несколько недостатков - 1) оно гораздо более многословно, требует больше строк кода. 2) необходимо, чтобы структура кода была определена глобально, потому что структуры шаблонов могут быть определены только глобально. 3) ему нужны все необходимые аргументы для работы кода, которые будут переданы/перенаправлены на вызов operator().

Вариант constexpr выглядит намного элегантнее и более интуитивно понятен в использовании. Есть ли способ решить такую ​​задачу с помощью if-constexpr? Чтобы заставить компилятор не компилировать ложные ветки во время компиляции.


person Arty    schedule 30.12.2020    source источник
comment
constexpr работает только в шаблонах функций и только тогда, когда условие зависит от параметра(ов) шаблона.   -  person NathanOliver    schedule 30.12.2020
comment
@mrks Нет, это не отвечает на мой вопрос. В основном из этого ответа я не понимаю, как я могу применить его к моему делу. Не могли бы вы преобразовать мой первый код в соответствии с вашим связанным ответом, чтобы он работал? Потому что, если это невозможно, мой вопрос следует открыть повторно.   -  person Arty    schedule 30.12.2020
comment
Почему бы не использовать конструкции препроцессора в стиле GNU autoconf?   -  person Basile Starynkevitch    schedule 30.12.2020
comment
@Arty: В вашем коде нет параметров шаблона, поэтому вы не можете создать свое условие на основе параметров шаблона. Таким образом, если единственный способ, с помощью которого constexpr if может отбросить операторы, состоит в том, чтобы сделать условие зависимым от параметров шаблона, это означает, что вы не можете делать то, что пытаетесь сделать с constexpr if. Это не макрос, и в обязанности constexpr if не входит отбрасывание всех операторов.   -  person Nicol Bolas    schedule 30.12.2020


Ответы (1)


Как упоминалось в комментариях, вы должны сделать отброшенный оператор зависит от параметров шаблона. То, что вы пытаетесь сделать, невозможно без введения шаблонов.

Например, это сработает, но это далеко не хорошее решение. Я добавил его как ответ, поскольку сходство с вопросом, который я пометил как дубликат, оспаривалось.

#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);
}
person mrks    schedule 30.12.2020
comment
Кстати, вместо (const auto x), вероятно, лучше использовать ссылку типа (auto const & x). - person Arty; 30.12.2020
comment
Спасибо, очень хорошее решение! Это работает для моего кода с дополнительным использованием std::remove_cvref_t. Принимаем и голосуем! - person Arty; 30.12.2020