为什么constexpr需要else才能工作?

时间:2018-12-17 15:50:18

标签: c++ templates c++17 template-meta-programming if-constexpr

我正尝试通过以下方式使用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...>();
    }
}

live demo

发生了什么事?在这种情况下,编译器为什么不能推断else

2 个答案:

答案 0 :(得分:12)

这是constexpr if上cppreference的摘录:

  

Constexpr如果   以constexpr开头的语句称为constexpr if语句。

     

在constexpr if语句中,condition的值必须是上下文转换为bool类型的常量表达式。如果值为true,则丢弃statement-false(如果存在),否则,丢弃statement-true。

很明显,两个分支中只有一个被丢弃。对于您而言,罪魁祸首代码无法丢弃,因为它位于else子句之外。

答案 1 :(得分:10)

if constexpr当该子句为true时,不会消除相应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。问题很多,收益很小。


编译器具有临时规则,可检测无法访问的代码等。这些不必像标准那样正式指定,它们在一个编译器之间可能会有所不同。

与此同时,每个编译器的标准必须相同。而且,消除和不消除的规则必须相同。

该标准应用了一条简单规则; ifelse块是唯一可以消除的候选对象。


因此标准不这样做。如果要消除代码,请将其放在if constexpr的{​​{1}}或else块中。语言开发资源最好用在收益更高且痛苦较小的事物上。