DoZerg
DoZerg

Reputation: 323

Short-circuit in noexcept expressions

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 it something C++ standard is missing?

Upvotes: 4

Views: 129

Answers (2)

user12002570
user12002570

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

Working demo

Upvotes: 4

cigien
cigien

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>()) 

demo

Upvotes: 8

Related Questions