if constexpr - 为什么完全检查丢弃的语句?
if constexpr - why is discarded statement fully checked?
我在 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 查找算法一样工作,但在元组上,而不是返回迭代器,它 returns 一个基于编译时谓词的可选索引。现在这段代码编译得很好并打印出
9
但是,如果元组不包含整数类型的元素,则程序无法编译,因为 i.value() 仍会在空可选上调用。为什么会这样?
这就是 constexpr if works. If we check [stmt.if]/2
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity ([temp.pre]), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.[...]
强调我的
所以我们可以看到,如果我们在模板中并且条件依赖于值,我们只会不评估丢弃的表达式。 main
不是函数模板,因此编译器仍会检查 if 语句的主体是否正确。
Cppreference 在他们关于 constexpr 的部分中也说了这一点:
If a constexpr if statement appears inside a templated entity, and if condition is not value-dependent after instantiation, the discarded statement is not instantiated when the enclosing template is instantiated .
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.
}
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:
void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}
我在 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 查找算法一样工作,但在元组上,而不是返回迭代器,它 returns 一个基于编译时谓词的可选索引。现在这段代码编译得很好并打印出
9
但是,如果元组不包含整数类型的元素,则程序无法编译,因为 i.value() 仍会在空可选上调用。为什么会这样?
这就是 constexpr if works. If we check [stmt.if]/2
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity ([temp.pre]), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.[...]
强调我的
所以我们可以看到,如果我们在模板中并且条件依赖于值,我们只会不评估丢弃的表达式。 main
不是函数模板,因此编译器仍会检查 if 语句的主体是否正确。
Cppreference 在他们关于 constexpr 的部分中也说了这一点:
If a constexpr if statement appears inside a templated entity, and if condition is not value-dependent after instantiation, the discarded statement is not instantiated when the enclosing template is instantiated .
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. }
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:
void f() { if constexpr(false) { int i = 0; int *p = i; // Error even though in discarded statement } }