【问题标题】:if constexpr - why is discarded statement fully checked?if constexpr - 为什么完全检查丢弃的语句?
【发布时间】:2020-04-11 02:48:59
【问题描述】:

我在 GCC 10 中使用 c++20 consteval 并编写了这段代码

#include <optional>
#include <tuple>
#include <iostream>

template <std::size_t N, typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if_impl(Predicate&& pred,
                                                  Tuple&& t) noexcept {
  constexpr std::size_t I = std::tuple_size_v<std::decay_t<decltype(t)>> - N;

  if constexpr (N == 0u) {
    return std::nullopt;
  } else {
    return pred(std::get<I>(t))
               ? std::make_optional(I)
               : find_if_impl<N - 1u>(std::forward<decltype(pred)>(pred),
                                      std::forward<decltype(t)>(t));
  }
}

template <typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if(Predicate&& pred,
                                             Tuple&& t) noexcept {
  return find_if_impl<std::tuple_size_v<std::decay_t<decltype(t)>>>(
      std::forward<decltype(pred)>(pred), std::forward<decltype(t)>(t));
}

constexpr auto is_integral = [](auto&& x) noexcept {
    return std::is_integral_v<std::decay_t<decltype(x)>>;
};


int main() {
    auto t0 = std::make_tuple(9, 1.f, 2.f);
    constexpr auto i = find_if(is_integral, t0);
    if constexpr(i.has_value()) {
        std::cout << std::get<i.value()>(t0) << std::endl;
    }
}

它应该像 STL 查找算法一样工作,但它在元组上工作,而不是返回迭代器,它返回一个基于编译时谓词的可选索引。现在这段代码编译得很好并打印出来了

9

但是,如果元组不包含整数类型的元素,则程序不会编译,因为 i.value() 仍然在空的可选项上调用。为什么会这样呢?

【问题讨论】:

标签: c++ c++20 if-constexpr


【解决方案1】:

这就是constexpr if 的工作原理。如果我们检查[stmt.if]/2

如果 if 语句的形式为 if constexpr,则条件的值应为 bool 类型的上下文转换的常量表达式;这种形式称为 constexpr if 语句。如果转换条件的值为假,则第一个子语句是丢弃的语句,否则第二个子语句(如果存在)是丢弃的语句。 在封闭模板化实体 ([temp.pre]) 的实例化期间,如果条件在其实例化后不依赖于值,则不实例化丢弃的子语句(如果有)。 [... ]

强调我的

所以我们可以看到,如果我们在模板中并且条件是值相关的,我们才不会评估丢弃的表达式。 main 不是函数模板,因此编译器仍会检查 if 语句的主体是否正确。

Cppreference 在他们关于 constexpr if with 的部分中也提到了这一点:

如果 constexpr if 语句出现在模板化实体中,并且 if 条件在实例化后不依赖于值,则在实例化封闭模板时不会实例化丢弃的语句。

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
    // ... handle p
    if constexpr (sizeof...(rs) > 0)
        g(rs...); // never instantiated with an empty argument list.
}

在模板之外,完全检查丢弃的语句。如果 constexpr 不能替代 #if 预处理指令:

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // Error even though in discarded statement
    }
}

【讨论】:

  • 您知道其中的原因吗?看起来这很适合 if constexpr。同样,解决方案是例如以某种方式将其包装在模板中?
  • @Yamahari 因为 C++ 模板的结构比您希望的要多和少。是的,将其包装在模板中(或像 i.value_or(0) 这样写)
  • @Yamahari 是的,解决方案是将代码放在函数模板中。就推理而言,我不知道为什么。这可能是一个很好的问题。
  • @Barry value_or(0) 工作正常,但对于元组大小为 0 的情况
猜你喜欢
  • 2011-07-03
  • 2021-07-31
  • 2015-07-08
  • 2021-11-19
  • 2018-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多