为什么这种检测习语的使用会导致Clang和GCC的编译错误不同,而MSVC不会导致编译错误

void_t尝试检测习惯用法时,我发现涉及减法运算符和 void* 的表达式会导致测试编译器中出现不同的错误。

GCC 10.2 及以下版本,无论使用 C++11、14、17 还是 20,似乎都将某些表达式视为硬错误而不是替换失败。

我正在使用以下(精简)示例

#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*");

并且希望代码能够针对所有四种语言标准进行编译而不会出错(包含std::void_tC++17 之前的替代品,因此可以测试更多标准)但是

  • GCC 10.2 及以下版本抱怨最后两个断言
  • Clang 没有抱怨
  • MSVC 抱怨 int*/void* 组合

我试图搜索现有的错误报告,但找不到。

我想知道哪个编译器是正确的,为什么。

这是一个神箭。

回答

GCC 10.2 及更早版本错误地将其(void*)0 - (void*)0视为硬错误而不是软错误。这是固定在后备箱中的。

Clang 做的一切都是正确的。

MSVC 是错误的允许(int*)0 - (void*)0;(同时正确拒绝(void*)0 - (int*)0;),如下所示:

[expr.add]/2.2

对于减法,应满足以下条件之一: ...

— 两个操作数都是指向相同完全定义对象类型的 cv 限定或 cv 非限定版本的指针;...


以上是为什么这种检测习语的使用会导致Clang和GCC的编译错误不同,而MSVC不会导致编译错误的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>