Почему оператор if constexpr не устраняет эту ошибку основного константного выражения?

Что касается этого вопроса. Выражение основной константы, которое используется для инициализации constexpr переменной y, имеет неправильный формат. Так много дано.

Но если я попытаюсь превратить if в if constexpr:

template <typename T>
void foo() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1 << x;
    }
}

int main(){
    foo<int>();
}

Ошибка сохраняется. С GCC 7.2 все еще дает:

error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]

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

Однако косвенное обращение через constexpr лямбда действительно помогает:

template <typename T>
void foo(){
    constexpr int x = -1;
    constexpr auto p = []() constexpr { return x; };
    if constexpr (x >= 0){
        constexpr int y = 1<<p();
    }
}

Спецификатор constexpr на y, похоже, меняет способ проверки отброшенной ветви. Это предполагаемое поведение?


@ max66 был достаточно любезен, чтобы проверить другие реализации. Он сообщает, что ошибка воспроизводится как с GCC (7.2.0 / Head 8.0.0), так и с Clang (5.0.0 / Head 6.0.0).


person StoryTeller - Unslander Monica    schedule 01.10.2017    source источник
comment
Похоже на ошибку компилятора. Вы пробовали другую реализацию?   -  person Shachar Shemesh    schedule 01.10.2017
comment
@ShacharShemesh - я сам не знал. Но OP сообщения, которое я связал, сообщает, что Clang и MSVC ведут себя одинаково.   -  person StoryTeller - Unslander Monica    schedule 01.10.2017
comment
Подтверждаю проблему с clang ++ 3.8.1   -  person max66    schedule 01.10.2017
comment
из wanbox я вижу, что проблема все еще сохраняется как с g ++ (7.2.0 и Head 8.0.0), так и с clang ++ (5.0.0 и Head 6.0.0).   -  person max66    schedule 01.10.2017
comment
Я не думаю, что спецификатор constexpr на y, кажется, меняет способ проверки отброшенной ветки, потому что удаляет это constexpr (просто определяя int y = 1 ‹< x; я больше не вижу ошибок (я полагаю, что это правильно, если y - инициализированная среда выполнения), но я вижу предупреждение (неиспользуемая переменная y); поэтому (если я не ошибаюсь) эта ветвь скомпилирована. Или, лучше, я думаю, что это правильно в том смысле, что с constexpr y инициализируется время компиляции (так 1 << x немедленно проверяется), а без инициализируется время выполнения.   -  person max66    schedule 01.10.2017
comment
@ max66 int y = 1 << x; - это просто неопределенное поведение. Добавление constexpr делает его некорректным, потому что UB не допускается в постоянном выражении.   -  person Oktalist    schedule 01.10.2017
comment
Когда возникает вопрос, почему компилятор диагностировал этот плохо сформированный код, который, как я надеялся, он не диагностирует, ответ - 90% случаев [temp.res] / 8.   -  person T.C.    schedule 02.10.2017
comment
@ T.C. - Забавное совпадение. Я процитировал именно этот абзац под ответом Дитмара. Как раз в то время, когда его ответ заставил меня уйти.   -  person StoryTeller - Unslander Monica    schedule 02.10.2017


Ответы (3)


В стандарте мало что говорится о заявлении об исключении if constexpr. По сути, в [stmt.if] есть два утверждения об этом:

  1. Во вмещающем шаблоне экземпляры отброшенных операторов не создаются.
  2. Имена, на которые ссылается отвергнутый оператор, не требуют определения ODR.

Ни то, ни другое не применимо к вашему использованию: компиляторы правильно жалуются на constexpr if инициализации. Обратите внимание, что вам нужно сделать условие зависимым от параметра шаблона, если вы хотите воспользоваться преимуществом создания экземпляра для сбоя: если значение не зависит от параметра шаблона, сбой происходит, когда шаблон определен. Например, этот код по-прежнему не работает:

template <typename T>
void f() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1<<x;
    }
}

Однако, если вы сделаете x зависимым от типа T, это нормально, даже если f создается с int:

template <typename T>
void f() {
    constexpr T x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1<<x;
    }
}
int main() {
    f<int>();
}
person Dietmar Kühl    schedule 01.10.2017
comment
Как я комментирую Керреку, пример в моем посте плохой. Я убедился, что он сохраняется после переноса кода в шаблон. Базовый вариант по-прежнему выдает ошибку. Косвенного обращения нет. Я обновил MCVE. Простите за неудобства. - person StoryTeller - Unslander Monica; 01.10.2017
comment
@StoryTeller: Я обновил свой ответ, чтобы уточнить, что затрагивается только создание шаблона. Ошибка, которую вы получаете, происходит во время определения шаблона! - person Dietmar Kühl; 01.10.2017
comment
@ DietmarKühl: Очень хороший момент - проверка независимых конструкций является частью определения шаблона, а не какого-либо экземпляра шаблона, поэтому отсутствие экземпляра отброшенной руки на самом деле является отвлекающим маневром. - person Kerrek SB; 01.10.2017
comment
Ммм .. Да, я начинаю понимать, где мое понимание пошло наперекосяк. Я также забыл, что, если специализация шаблона недопустима, для любого аргумента программа плохо сформирована, диагностика не требуется. Это полностью разрешило мое замешательство. - person StoryTeller - Unslander Monica; 01.10.2017

Обратите внимание, что для утверждения, отклоненного Constexpr If:

отвергнутое утверждение не может быть неправильно сформировано для каждой возможной специализации:

Чтобы исправить проблему, вы можете сделать заявление в зависимости от параметра шаблона, например

template<typename T, int X> struct dependent_value { constexpr static int V = X; };

template <typename T>
void foo() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1 << dependent_value<T, x>::V;
    }
}

ЖИТЬ

person songyuanyao    schedule 01.10.2017

Я не уверен, почему вы ожидаете, что ветка не будет проверена. Единственный раз, когда ветвь if "не проверена", - это когда она является частью шаблона, а не создана, согласно [stmt.if] p2:

Во время создания экземпляра включающего шаблонного объекта (раздел 17), если условие не зависит от значения после его создания, отвергнутое подзаполнение (если оно есть) не создается.

Кажется, ваш код не подходит для этой ситуации.

person Kerrek SB    schedule 01.10.2017
comment
Я признаю, что мой минимально воспроизводимый пример является плохим. Но ошибка сохраняется после переноса кода в шаблон (но не с лямбда-выражением). Так что я обновлю свой вопрос. Извините за недействительность этого ответа. - person StoryTeller - Unslander Monica; 01.10.2017
comment
@StoryTeller: Не беспокойся. Хорошо изучать такие новые функции подробно. - person Kerrek SB; 01.10.2017