2

While playing around with the detection idiom and void_t I found that expressions where the subtraction operator and void* are involved lead to different errors in the tested compilers.

GCC 10.2 and below, no matter if C++11,14,17 or 20 is used, seem to treat some expressions as hard error rather then substitution failures.

I'm working with the following (striped down) example

#include <type_traits>

template< class, class, class = void >
struct has_minus_void_t : std::false_type {};
template<class T, class U>
struct has_minus_void_t<T, U, std::void_t<
    decltype(T() - U())
    >> : std::true_type {};

static_assert(has_minus_void_t<int*,int*>::value, "int*");
static_assert(!has_minus_void_t<int*,void*>::value, "intvoid*");
static_assert(!has_minus_void_t<void*,int*>::value, "voidint*");
static_assert(!has_minus_void_t<void*,void*>::value, "void*");

and would expect the code to compile without errors for all four language standards (contains a std::void_t replacement for pre C++17 so more standards can be tested) but

  • GCC 10.2 and below complain about the last two asserts
  • Clang doesn't complain
  • MSVC complains about the int*/void* combination

I tried to search for an existing bug report but couldn't find one.

I'm wondering which compiler is right and why.

Here's a godbolt.

jesses
  • 559
  • 3
  • 15
  • Some of this has changed since C++17 I think. Your demo compiles with C++11, and C++20, but it's not clear in your question which revsions you're referring to. There also seem to be differences between gcc10 and gcc-trunk in how they compile pre c++17 and post-c++17 as well, which is missing from the question. – cigien Apr 04 '21 at 19:48
  • Is that `Instantiate()` doing anything? Is there a difference to just `decltype(T() - U())` instead of `decltype(Instantiate() - Instantiate())`? It seems the same. – KamilCuk Apr 04 '21 at 19:59
  • @cigien i updated the question but i don't understand how the difference between gcc10 and trunk applies to the question because i want to know what's the expected behavior according to the standard. Here's a gcc-only godbolt: https://godbolt.org/z/8jWvWs9b5 – jesses Apr 04 '21 at 20:02
  • @KamilCuk it is; i edited the question and updated the godbolt example – jesses Apr 04 '21 at 20:04
  • Och. But `it is` yet you removed it, so.. it is not? – KamilCuk Apr 04 '21 at 20:05
  • @cigien with GCC i cannot get the demo compiled for C++11 and C++20. What compiler/version have you used? – jesses Apr 04 '21 at 20:06
  • 1
    @KamilCuk it is [the same]; at least for this example. For some combinations a ref should be added. – jesses Apr 04 '21 at 20:07
  • `below complain about all four asserts` `static_assert(has_minus::value, "int*");` should be fine, does it for all four? After leaving only `static_assert(has_minus::value, "int*");` it compiles fine in that godbolt. – KamilCuk Apr 04 '21 at 20:08
  • No, I don't mean any version of GCC compiles the code, I just meant your demo includes both C++11, and C++20. – cigien Apr 04 '21 at 20:10
  • @KamilCuk you're right. Sorry. Fixed the question. – jesses Apr 04 '21 at 20:10
  • @KamilCuk I edited the question and explicitly mentined C++11,14,17,20 now. – jesses Apr 04 '21 at 20:14

1 Answers1

5

GCC 10.2 and earlier are wrong to treat (void*)0 - (void*)0 as a hard instead of soft error. This is fixed in trunk.

Clang does everything correctly.

MSVC is wrong to allow (int*)0 - (void*)0; (while correctly rejecting (void*)0 - (int*)0;), as shown by:

[expr.add]/2.2

For subtraction, one of the following shall hold: ...

— both operands are pointers to cv-qualified or cv-unqualified versions of the same completely-defined object type; ...

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207