Почему if constexpr требует для работы else?

Я пытаюсь использовать if constexpr следующим образом:

template<template <typename First, typename Second> class Trait,
    typename First, typename Second, typename... Rest>
constexpr bool binaryTraitAre_impl()
{
    if constexpr (sizeof... (Rest) == 0)
    {
        return Trait<First, Second>{}();    
    }
    return Trait<First, Second>{}() and binaryTraitAre_impl<Trait, Rest...>();
}

Пример использования:

static_assert(binaryTraitAre_impl<std::is_convertible,
    int, int&,
    int*, void*>());

Но это не скомпилировать

лязгать:

error: no matching function for call to 'binaryTraitAre_impl'
        return Trait<First, Second>{}() and binaryTraitAre_impl<Trait, Rest...>();
                                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

gcc:

prog.cc: In instantiation of 'constexpr bool binaryTraitAre_impl() [with Trait = std::is_convertible; First = int*; Second = void*; Rest = {}]':
prog.cc:9:80:   required from 'constexpr bool binaryTraitAre_impl() [with Trait = std::is_convertible; First = int; Second = int&; Rest = {int*, void*}]'
prog.cc:15:83:   required from here
prog.cc:9:80: error: no matching function for call to 'binaryTraitAre_impl<template<class _From, class _To> struct std::is_convertible>()'
    9 |         return Trait<First, Second>{}() and binaryTraitAre_impl<Trait, Rest...>();
      |                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
prog.cc:3:17: note: candidate: 'template<template<class First, class Second> class Trait, class First, class Second, class ... Rest> constexpr bool binaryTraitAre_impl()'
    3 |  constexpr bool binaryTraitAre_impl()
      |                 ^~~~~~~~~~~~~~~~~~~
prog.cc:3:17: note:   template argument deduction/substitution failed:
prog.cc:9:80: note:   couldn't deduce template parameter 'First'
    9 |         return Trait<First, Second>{}() and binaryTraitAre_impl<Trait, Rest...>();
      |                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

Но я обнаружил, что ошибка исчезает после добавления else:

template<template <typename First, typename Second> class Trait,
    typename First, typename Second, typename... Rest>
constexpr bool binaryTraitAre_impl()
{
    if constexpr (sizeof... (Rest) == 0)
    {
        return Trait<First, Second>{}();
    }
    else
    {
        return Trait<First, Second>{}() and binaryTraitAre_impl<Trait, Rest...>();
    }
}

живая демонстрация

Что случилось? Почему в этом случае компилятор не может определить else?

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

Rakete1111 17.12.2018 16:54

@Rakete не смог найти его на странице тега if-constexpr (хорошо, я просмотрел только первую страницу). Если найдешь, удалю свой ответ.

Matthieu Brucher 17.12.2018 16:56

@MatthieuBrucher то же самое.

Rakete1111 17.12.2018 16:57

Если у него есть официальная идиома, он будет более дружественным к SEO

Chen Li 17.12.2018 16:58

@ Rakete1111 Да, мне кажется, что я уже встречал подобный вопрос раньше, но мне всегда трудно найти хорошие обманы (вот почему я в конечном итоге прибегаю к тому, чтобы отдавать предпочтение обычным обманам).

Barry 17.12.2018 17:01

Обратите внимание, что ваша реализация неверна - это даст мне true для binaryTraitAre_impl<is_same, int, double>() (это второстепенно для вопроса в целом)

Barry 17.12.2018 17:06

@ Барри, спасибо! Позже я добавлю больше тестовых примеров локально.

Chen Li 17.12.2018 17:08

@Barry Я, наверное, сохраню это как ссылку для таких дубликатов.

Matthieu Brucher 17.12.2018 17:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
8
2 035
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это отрывок из cppreference на constexpr if:

Constexpr If The statement that begins with if constexpr is known as the constexpr if statement.

In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool. If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

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

if constexpr, когда предложение истинно, не исключает код вне соответствующего блока else.

Вы можете расширить C++ для этого, но это быстро становится проблемой. Очевидны только самые тривиальные случаи, и указывать, что это за тривиальные случаи, очень сложно. Я имею в виду, вы покрываете:

if constexpr( blah ){
  if (true) return 7;
}

? Как насчет

if constexpr( blah ){
  if (blah) return 7;
  else exit(-1);
}

? Или

if constexpr( blah ){
  if (blah) return 7;
  else return false;
}

или

if constexpr( blah ){
  if (blah) goto foo;
  return false;
foo: return true;
}

или как насчет:

if constexpr( blah ){
  std::size_t count = 0;
  while (foo != 1 && (++count < (std::size_t)-1))
    switch (foo%2) {
      case 1: foo = 3*foo+1;
      case 0: foo = foo/2;
    }
  }
  if (count < (std::size_t)-1) return true;
}

? Я могу привести почти континуум случаев, которые более или менее «очевидны» в том, что они никогда не вернутся. А расплата? Отсутствие else. Много проблем, мало пользы.


У компиляторов есть специальные правила для обнаружения недоступного кода и т.п. Их не нужно указывать так формально, как в стандарте, и они могут отличаться от одного компилятора к другому.

Между тем стандарт должен быть одинаковым для всех компиляторов. И правила в отношении того, что удаляется, а что нет, должны быть идентичными.

Стандарт применяет простое правило; блоки if и else - единственные кандидаты на удаление.


Так что стандарт этого не делает. Если вы хотите, чтобы код был исключен, поместите его в блок if constexpr или elseif constexpr. Ресурсы по языковому развитию лучше тратить на вещи, которые приносят больше пользы и менее болезненны.

Другие вопросы по теме