如果constexpr-为什么要完全检查废弃的语句?


14

我在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()仍在空的可选字段上调用。现在为什么呢?



@AndyG不能解决问题,对吗?x)
Yamahari

Answers:


11

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

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

重点矿

因此,我们可以看到,只有在模板中并且条件与值相关时,我们才不对舍弃的表达式求值。 main不是函数模板,因此编译器仍会检查if语句的主体是否正确。

Cppreference在有关constexpr的部分中也这样说:

如果constexpr 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
    }
}

你知道这个的原因吗?看起来如果constexpr很适合。同样,解决方案将是例如以某种方式将其包装在模板中?
Yamahari

@Yamahari因为C ++模板的结构比您想要的要多或少。是的,将其包装在模板中(或写为i.value_or(0)
Barry

2
@Yamahari是的,解决方案是将代码放在函数模板中。就推理而言,我不知道为什么。这可能是一个很好的问题。
NathanOliver

@Barry value_or(0)可以正常工作,但对于元组大小为0的情况
Yamahari

@Yamahari是的...对我来说不是一个好建议。
巴里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.