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
I tried to search for an existing bug report but couldn't find one.
I'm wondering which compiler is right and why.
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; ...