9

I noticed a discrepancy between C++23 and C++26 code when using googlemock:

enum A { A1, A2 };
enum B { B1, B2 };
TEST(...)
{
   ASSERT_EQ(A1, B1); // compiles fine in C++23 and C++26
   ASSERT_THAT(A1, Eq(B1)); // does not compile in C++26
}

/opt/compiler-explorer/libs/googletest/trunk/googletest/include/gtest/gtest-matchers.h:700:16: error: no match for call to '(std::equal_to) (const A&, const B&)'

700 | return Op()(lhs, Unwrap(rhs_));

..compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/stl_function.h:496:9: note: template argument deduction/substitution failed:

This simplified google mock code translates to use A1 == B1 and std::equal_to<void>{}(A1, B1):

enum A { A1, A2 };
enum B { B1, B2 };
void foo()
{
   // ASSERT_EQ(A1, B1); 
   if (A1 == B1) { ... }  // compiles fine in C++23 and C++26
   
   // ASSERT_THAT(A1, Eq(B1)); 
   if (std::equal_to<void>{}(A1, B1)) { ... } // does not compile in C++26
}

See godbolt.

This only happens in gcc(g++) -- clang compiles both versions.

It is, of course, a bug in test to compare values from different enums, and it is perfect that std::equal_to<void>{} catches that.

Is this behavior mandated by C++26, or just some kind of "bug" in gcc's std lib? It works that way only in gcc(g++). Clang compiles fine both ways in C++23 and C++26.

Will A1 == B1 produce compilation errors as well in the final version of C++26, or it is not going to change?

1 Answer 1

14

Comparison between different enumeration types is deprecated in C++20 (by P1120R0) and disallowed in C++26 (by P2864R2).

[expr.eq]:

If both operands are of arithmetic or enumeration type, the usual arithmetic conversions ([expr.arith.conv]) are performed on both operands; each of the operators shall yield true if the specified relationship is true and false if it is false.

[expr.arith.conv]:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

  • [...]
  • Otherwise, if one operand is of enumeration type and the other operand is of a different enumeration type or a floating-point type, the expression is ill-formed.

Both GCC and Clang emit a warning/error for A1 == B1 in C++26. There's no error for ASSERT_EQ(A1, B1) because the googletest header is treated as a system header, which causes the error to be suppressed.

Sign up to request clarification or add additional context in comments.

7 Comments

But why the difference between == and std::equal_to<void>{}
It is easier to update standard library then compiler, so std::equal_to<void> was updated first. Since C++26 is not official yet, compilers are simply catching on.
Actually both GCC and Clang diagnose ==, and GCC also diagnoses std::equal_to<void>{}: godbolt.org/z/6TEvjbdqv It seems that Clang still permits A1 == B1 for SFINAE's sake, and both compilers are silent on ASSERT_EQ because the called function is in a system header.
GCC and Clang allow lots of non-standard extensions in non-templated code (because the alternative is that the code doesn't compile at all) that they don't allow in templated code (because the alternative might compile if it's in a SFINAE check for example), and std::equal_to<void>::operator()(auto&&, auto&&) is templated
@Jarod42 It seems that that decltype defining the return type causes template argument deduction failure: godbolt.org/z/GqM5ff5v4.
BTW, you don't need forwards godbolt.org/z/qG99PWbdT
@Artyer Your comment is the actual answer, is it not? The confusion about this question, to me at least, is: > why does it matter if the == is spelled out, macroed, templated or macroed and templated?' <

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.