Reputation: 323
given the follow code:
// g++ main.cpp -std=c++23
#include <type_traits>
using namespace std;
template<typename P>
concept pointer_like = is_pointer_v<P> || requires (P p) { p.operator ->(); };
template<pointer_like P>
constexpr auto func(P p)
noexcept(is_pointer_v<P> || noexcept(p.operator ->())) // error: 'int *' doesn't have 'operator ->'
{
if constexpr (is_pointer_v<P>)
return p;
else
return p.operator ->();
}
int main() {
int i = 0;
auto pi = func(&i); // error from here
}
https://godbolt.org/z/Gqq3W4o4h
It seems short-circuit is not performed in the noexcept
expression:
noexcept(is_pointer_v<P> || noexcept(p.operator ->()))
My expectation is:
is_pointer_v<P>
is true so operator ->
is not checked;operator ->
is valid.Is it something C++ standard is missing?
Upvotes: 4
Views: 129
Reputation: 1
Each sub-expression in the noexcept
must be valid individually/separately.
You can use requires
to achieve the desired effect/result as shown below:
template<pointer_like P> constexpr auto func(P p)
noexcept(requires { requires is_pointer_v<P> || noexcept(p.operator ->()); })
{
if constexpr (is_pointer_v<P>)
return p;
else
return p.operator ->();
}
Upvotes: 4
Reputation: 60228
The expression in the noexcept
needs to be well-formed. The short-circuiting of logical operators only applies to the evaluation of the operands, not to their well-formedness.
The problem can be resolved with another level of indirection that correctly handles the ill-formed operands:
template<pointer_like P>
consteval auto noexcept_check()
{
if constexpr (is_pointer_v<P>)
return true;
else
return noexcept(P{}.operator ->());
}
and then the declaration of func
is simply:
template<pointer_like P>
constexpr auto func(P p)
noexcept(noexcept_check<P>())
Upvotes: 8