Why std::expected's `operator==` is not SFINAE-friendly?
Asked Answered
E

1

6

I'm reading the documentation on std::expected::operator==, and apparently it's not SFINAE-friendly. Instead of "doesn't participate in overload resolution if [types are not eq-comparable]", it says "the program is ill-formed [in that case]".

And indeed:

#include <concepts>
#include <expected>

struct A {};

static_assert(!std::equality_comparable<std::expected<A, A>>);

This assertion fails on MSVC and Clang with libc++, and causes a hard error on Clang with MSVC STL.

But why is it not SFINAE-friendly?

std::expected<T,U> requires both T and U to be complete anyway, so seemingly nothing is stopping all operations on it to be SFINAE-friendly.

Eward answered 20/9 at 11:16 Comment(4)
And I believe tl::expected's == is SFINAE-friendly, so it looks possible.Eward
Just a thought, might this depend on struct A (which is empty here)?Gerardogeratology
@S.Gleissner What do you mean? The point is that if A lacks ==, then std::expected's == must be disabled, so std::equality_comparable must return false for it. But here it returns true...Eward
Assertion fails in GCC/libstdc++ as well: gcc.godbolt.org/z/6859PhcvhConstitute
C
4

I think it was simply overlooked.

Until April 2024 the operator== of similar library types like std::pair, std::tuple, std::optional, std::variant etc. were also not SFINAE-friendly in this sense either.

They were made SFINAE-friendly with P2944R3 which also notes that this probably just happened without any intention behind it.

The wording changes are listed in the paper starting from here. If a clause originally said "Mandates:" that means that if the condition was not satisfied the program was ill-formed (i.e. a hard error). If it originally said "Preconditions:" then not satisfying the condition caused the program to have undefined behavior. When it now says "Constraints:" instead, then not satisfying the condition means that the overload does not participate in overload resolution (i.e. it is SFINAE-friendly).

My guess would be that std::expected was similarly forgotten.

Claudioclaudius answered 20/9 at 11:50 Comment(4)
And library implementers just rolled along with it. Bah!Eward
@Eward At the time they implemented it, it was the same behavior for all of the library types. And historically operators like this were never constrained. This started to become more widespread in the library only after C++20 I think.Claudioclaudius
Maybe @Barry will see this post and can comment on whether my guess is correct. The paper is authored by him.Claudioclaudius
Yeah just forgot about expected. Wakely is fixing it in P3379.Oquinn

© 2022 - 2024 — McMap. All rights reserved.