Date: Wed, 16 Nov 2022 11:59:53 +0200
Hi,
I was wondering why the language doesn't support short-circuiting on
logical operations in constexpr context.
It would be very useful to avoid evaluating certain compile-time
expressions if they are not needed for the result of the logic expression,
especially if they are ill-formed.
Consider the following code snippet:
```
#include <type_traits>
template<typename F, typename T>
void foo(F f, T t)
{
constexpr bool test = std::is_invocable_v<F, T> &&
std::is_same_v<std::invoke_result_t<F, T>, int>;
if constexpr ( test ) { /*do something*/ }
else { /*do something else*/ }
}
void test()
{
foo([](){}, 42); //error: no type named 'type' in 'struct
std::invoke_result<main()::<lambda()>, int>'
}
```
I know that for this particular case we have `is_invocable_r` but the code
is just for illustrative purposes. I'm talking in general.
The way we have to do it currently is like this (if we ignore
is_invocable_r) :
```
template<typename F, typename T>
void foo(F f, T t)
{
if constexpr ( std::is_invocable_v<F, T> )
{
constexpr bool test = std::is_same_v<std::invoke_result_t<F, T>, int>;
if constexpr ( test ) { /*do something*/ }
else { /*do something else*/ }
}
else { /*do something else*/ } //not DRY
}
```
The main problem is we have to repeat the else statement. And if we have
several such checks the amount of if-else branches grows exponentially.
I don't have much experience with concepts and I'm not sure if they can
help in this situation. But even if they can, we'll still have to write
ad-hoc concepts for each place where we need this kind of short-circuiting.
I know the short answer to my question is "because it doesn't" but is there
a reason they decided to go with the full evaluation rather than the
short-circuiting evaluation?
Runtime evaluation of logical expressions still uses short-circuiting and
many programmers use it to their advantage by writing unsafe operations
that are guarded by safety checks in one expression.
Thanks,
Zamfir Yonchev
I was wondering why the language doesn't support short-circuiting on
logical operations in constexpr context.
It would be very useful to avoid evaluating certain compile-time
expressions if they are not needed for the result of the logic expression,
especially if they are ill-formed.
Consider the following code snippet:
```
#include <type_traits>
template<typename F, typename T>
void foo(F f, T t)
{
constexpr bool test = std::is_invocable_v<F, T> &&
std::is_same_v<std::invoke_result_t<F, T>, int>;
if constexpr ( test ) { /*do something*/ }
else { /*do something else*/ }
}
void test()
{
foo([](){}, 42); //error: no type named 'type' in 'struct
std::invoke_result<main()::<lambda()>, int>'
}
```
I know that for this particular case we have `is_invocable_r` but the code
is just for illustrative purposes. I'm talking in general.
The way we have to do it currently is like this (if we ignore
is_invocable_r) :
```
template<typename F, typename T>
void foo(F f, T t)
{
if constexpr ( std::is_invocable_v<F, T> )
{
constexpr bool test = std::is_same_v<std::invoke_result_t<F, T>, int>;
if constexpr ( test ) { /*do something*/ }
else { /*do something else*/ }
}
else { /*do something else*/ } //not DRY
}
```
The main problem is we have to repeat the else statement. And if we have
several such checks the amount of if-else branches grows exponentially.
I don't have much experience with concepts and I'm not sure if they can
help in this situation. But even if they can, we'll still have to write
ad-hoc concepts for each place where we need this kind of short-circuiting.
I know the short answer to my question is "because it doesn't" but is there
a reason they decided to go with the full evaluation rather than the
short-circuiting evaluation?
Runtime evaluation of logical expressions still uses short-circuiting and
many programmers use it to their advantage by writing unsafe operations
that are guarded by safety checks in one expression.
Thanks,
Zamfir Yonchev
Received on 2022-11-16 10:00:06