【问题标题】:Why does if constexpr require an else to work?为什么 if constexpr 需要 else 才能工作?
【发布时间】:2019-05-18 00:29:50
【问题描述】:

我正在尝试通过以下方式使用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

【问题讨论】:

  • 这类问题有骗人的吗?似乎if constexpr 是一个反直觉的功能,很多人都弄错了。我很确定我已经看到过这样的问题......
  • @Rakete 在 if-constexpr 标记页面中找不到一个(好吧,我只查看了第一页)。如果你找到了,我会删除我的答案。
  • @MatthieuBrucher 一样。
  • @Rakete1111 是的,我觉得我以前见过这样的问题 - 但我总是很难找到好的骗子(这就是为什么我最终求助于偏爱常见的骗子)。
  • @Barry 我现在可能会将其存储为此类重复的参考。

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


【解决方案1】:

这是constexpr if上cppreference的摘录:

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

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

很明显,只有两个分支中的一个被丢弃。在您的情况下,不能丢弃罪魁祸首代码,因为它在 else 子句之外。

【讨论】:

    【解决方案2】:

    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。问题多,收益少。


    编译器有专门的规则来检测无法访问的代码等。这些不必像标准那样正式指定,它们可能因编译器而异。

    与此同时,每个编译器的标准必须相同。淘汰和淘汰的规则必须相同。

    该标准应用了一个简单的规则; ifelse 块是唯一的消除候选者。


    所以标准没有这样做。如果您想消除代码,请将其放在if constexprif constexprelse 块中。语言开发资源最好用在产出更高、痛苦更少的事情上。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 2012-07-13
    • 2017-08-14
    • 1970-01-01
    相关资源
    最近更新 更多