Почему, если constexpr требует еще работать? - PullRequest
0 голосов
/ 17 декабря 2018

Я пытаюсь использовать 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*>());

Но это не скомпилирует

clang :

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 в этом случае?

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

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 или else блок if constexpr.Ресурсы по развитию языка лучше расходуются на вещи, которые приносят больше прибыли и являются менее болезненными.

0 голосов
/ 17 декабря 2018

Это выдержка из cppreference в constexpr if:

Constexpr If. Оператор, который начинается с if constexpr, известен как оператор constexpr if.

В constexpr ifоператор, значение условия должно быть контекстно-преобразованным константным выражением типа bool.Если значение равно true, тогда оператор-ложь отбрасывается (если присутствует), в противном случае оператор-истина отбрасывается.

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

...