constexpr 唯一 ID,使用 clang 编译,但不使用 GCC 编译



早上,伙计们!

我正在重构事件队列。我正在四处寻找是否可以在编译时使事件 ID 唯一。我想出的适用于 clang 4.0.0,但在 g++ 6.3.1 中给出了编译错误。

这个想法是使用静态成员变量的地址来唯一标识各个类型,然后使用标记从类模板生成这些唯一类型。

使用静态成员的地址作为类型 ID 是一种相当常见的技术,但使用模板来执行此操作意味着清除 ODR。MSN 在这里引用标准来表明这是一种有效的方法: 编译时常量 id

我的问题是做这个 constexpr。如果我删除 constexpr 并在运行时对其进行测试,则一切按预期工作。但是,这样做 constexpr 会失败 g++ 中的静态断言,即"错误:静态断言的非常量条件"。

经过相当多的研究,似乎最相似的问题是:

  • 指针算术或调用非 constexpr 成员:为什么这不是一个常量表达式? 没有指针算术,一切都是 constexpr。
  • 在 constexpr 表达式中使用 reinterpret_cast:Constexpr 指针值 这是使用指针,但类型是逐字的;因此不会应用任何转换。
  • 使用不完整的类型:为什么这个 constexpr 静态成员函数在调用时不被视为 constexpr? 我有理由确定这种类型是完整的。

这些问题中的大多数是 g++ 不合格和 clang++ 错误。这是相反的。

我被难住了。这是我所得到的精简版本,在静态断言中用 g++ 无法编译的内容进行了注释:

template <typename tag>
struct t
{
constexpr static char const storage{};
};
template <typename tag>
constexpr char const t<tag>::storage;
struct tag_0 {};
struct tag_1 {};
static_assert(&t<tag_0>::storage == &t<tag_0>::storage, "This always compiles.");
static_assert(&t<tag_1>::storage == &t<tag_1>::storage, "So does this.");
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++.");
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this.");
constexpr auto id_0 = &t<tag_0>::storage; // This does.
constexpr auto id_1 = &t<tag_1>::storage; // This does.
static_assert(id_0 != id_1, "This also does not.");

以下是编译器输出:

~$ clang++ --version
clang version 4.0.0 (tags/RELEASE_400/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
~$ clang++ -std=c++14 -c example.cpp
~$ g++ --version
g++ (GCC) 6.3.1 20170306
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
example.cpp:14:1: error: non-constant condition for static assertion
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++.");
^~~~~~~~~~~~~
example.cpp:14:34: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++.");
~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
example.cpp:15:1: error: non-constant condition for static assertion
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this.");
^~~~~~~~~~~~~
example.cpp:15:15: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this.");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cpp:19:1: error: non-constant condition for static assertion
static_assert(id_0 != id_1, "This also does not.");
^~~~~~~~~~~~~
example.cpp:19:20: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression
static_assert(id_0 != id_1, "This also does not.");
~~~~~^~~~~~~
~$ 

我很好奇为什么这种特定的方法不能用 gcc 编译,而是用 clang 编译,因为这与我对 constexpr 的理解相冲突。

(我不是在问这是否是一个好的设计,或者是否有其他方法可以实现这一点。我还有其他方法可以做到这一点。

谢谢!

编辑:没有模板的可比较示例可以使用两个编译器进行编译可能是:

struct t1
{
static constexpr int const v{}; 
};
constexpr int t1::v;
struct t2
{
static constexpr int const v{}; 
};
constexpr int t2::v;
static_assert(&t1::v != &t2::v, "compiles with both");

您正在尝试将两个指向类成员的不同指针作为constexpr进行比较,并且如果编译器应将其评估为constexpr(对象的地址可能还不知道?MSVC和clang这样做,gcc没有。定义了指针指向自身的比较结果。

最新更新