为什么 if constexpr 需要 else 才能工作?
Why does if constexpr require an else to work?
我正在尝试按以下方式使用 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...>();
}
}
发生了什么事?为什么编译器不能在这种情况下推断出 else
?
这是 constexpr if
上的 cppreference 的摘录:
Constexpr If
The statement that begins with if constexpr is known as the constexpr if statement.
In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool. If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.
很明显,两个分支中只有一个被丢弃了。在您的情况下,不能丢弃罪魁祸首代码,因为它在 else
子句之外。
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;
}
?我可以想出一个近乎连续的案例,这些案例在他们的 never-return 中稍微或多或少 "obvious"。和回报?没有 else
。问题多,收益少。
编译器有专门的规则来检测无法访问的代码等。这些不必像标准那样正式指定,并且它们可能因编译器而异。
同时,每个编译器的标准必须相同。消除和不消除的规则必须相同。
该标准适用一个简单的规则; if
和 else
块是唯一要消除的候选对象。
所以标准没有这样做。如果要删除代码,请将其放在 if constexpr
的 if constexpr
或 else
块中。语言开发资源最好花在收益更高、痛苦更少的事情上。
我正在尝试按以下方式使用 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...>();
}
}
发生了什么事?为什么编译器不能在这种情况下推断出 else
?
这是 constexpr if
上的 cppreference 的摘录:
Constexpr If The statement that begins with if constexpr is known as the constexpr if statement.
In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool. If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.
很明显,两个分支中只有一个被丢弃了。在您的情况下,不能丢弃罪魁祸首代码,因为它在 else
子句之外。
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;
}
?我可以想出一个近乎连续的案例,这些案例在他们的 never-return 中稍微或多或少 "obvious"。和回报?没有 else
。问题多,收益少。
编译器有专门的规则来检测无法访问的代码等。这些不必像标准那样正式指定,并且它们可能因编译器而异。
同时,每个编译器的标准必须相同。消除和不消除的规则必须相同。
该标准适用一个简单的规则; if
和 else
块是唯一要消除的候选对象。
所以标准没有这样做。如果要删除代码,请将其放在 if constexpr
的 if constexpr
或 else
块中。语言开发资源最好花在收益更高、痛苦更少的事情上。