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?
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 ->();
}