clang жалуется на функцию constexpr в случае инструкции switch

#c #c 11

#c #c 11

Вопрос:

 struct X
    {
    enum class E
    {
        A,B
    };

    static constexpr X A()
    {
        return X{E::A};
    }

    static constexpr X B()
    {
        return X{E::B};
    }

    constexpr operator E() const
    {
        return a;
    }
    E a;
};

template <typename T>
struct Y
{
    void f()
    {
        // without this line clang errs
        // const auto amp; x = this->x;
        switch(x)
        {
            case X::A():
            case X::B():
            default: return;
        }
    }

    X x = X::A();
};

int main()
{
    Y<int>{}.f();
}
  

Без отмеченной строки во фрагменте clang выдает следующую ошибку:

ошибка: значение case не является регистром константного выражения

X::B():

Однако я попробовал gcc, и он скомпилировался нормально. Кто-нибудь знает, gcc проявляет снисходительность или у clang есть какая-то ошибка?

Смотрите на godbolt (clang 8.0.0):https://godbolt.org/z/ETe5WQ Однако (gcc 8.3) отлично компилируется (также на godbolt) и пробовал другие версии gcc, и они также были в порядке

Обновить:

обнаружена ошибка

Комментарии:

1. @mkmostafa если вы подозреваете ошибку компилятора, не помешало бы указать версии компилятора, которые вы использовали как gcc, так и clang.

2. Он компилируется, если вы меняете switch (x) на switch(this-> x)

3. Также компилируется с clang 5 и clang 6: godbolt.org/z/KHMnoX Я предлагаю сообщить об ошибке по адресу bugs.llvm.org

Ответ №1:

У Clang (8.0.0) здесь ошибка.

Если вы пишете constexpr auto A = X::A(); и используете case A: в своей switch инструкции, вы получаете ту же ошибку компиляции (говорящую, что это A не постоянное выражение).

Однако, если вы удалите регистры, он отлично компилируется (что подразумевает, что A является допустимым constexpr => противоречит предыдущей ошибке).

Более того, switch(x) сбой при switch(this->x) успешном выполнении. Поскольку x == this->x в вашем случае это определенно ошибка.

Как упоминал чтз, clang (5/6), похоже, работает просто отлично. Это не аргумент, а очевидная регрессия.

Обновление: Как упоминалось в OP, они отправили отчет об ошибке.

Комментарии:

1. Эта ошибка также затрагивает версии Clang с 7.0.0 по 9.0.1 и, похоже, исправлена в версии 10.0.0. По крайней мере, это то, что я получил, вставляя код из отчета об ошибке в godbolt.

Ответ №2:

Похоже, что clang не работает, switch(x) это переключатель в перечислении X::E .

Если вы добавляете явное приведение к X::E ( static_cast или в стиле C или что-то еще), ваш код компилируется без ваших изменений.

Это происходит только тогда, когда ваш класс является template .

Использование switch(this->x) также работает.

Поскольку всякий раз, когда x является членом класса, x это просто другое название для this->x даже в template , это должно быть ошибкой clang.

Правила того, как вы можете выполнить переключение для не-перечисляемого / целочисленного типа, интересны тем, что они основаны на существовании неопределенного оператора приведения к любому перечисляемому или целочисленному типу в switch выражении, а затем вызывают то же приведение в case выражении.