C++ constexpr唯一id,使用clang编译,但不使用gcc

C++ constexpr唯一id,使用clang编译,但不使用gcc,c++,c++11,gcc,constexpr,C++,C++11,Gcc,Constexpr,早上好,各位 我正在重构一个事件队列。我想看看是否可以在编译时使事件ID唯一。我提出的方法适用于Clang4.0.0,但在g++6.3.1中给出了一个编译错误 其思想是使用静态成员变量的地址来唯一标识单个类型,然后使用标记从类模板生成这些唯一类型 使用静态成员的地址作为类型id是一种相当常见的技术,但使用模板来实现这一点意味着要避开ODR。MSN引用此处的标准来说明这是一种有效的方法: 我的问题是做这个constexpr。如果我删除constexpr并在运行时测试它,一切都会按预期工作。但是,

早上好,各位

我正在重构一个事件队列。我想看看是否可以在编译时使事件ID唯一。我提出的方法适用于Clang4.0.0,但在g++6.3.1中给出了一个编译错误

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

使用静态成员的地址作为类型id是一种相当常见的技术,但使用模板来实现这一点意味着要避开ODR。MSN引用此处的标准来说明这是一种有效的方法:

我的问题是做这个constexpr。如果我删除constexpr并在运行时测试它,一切都会按预期工作。但是,执行此constexpr会使g++中的静态断言失败,该断言说:“错误:静态断言的非常量条件”

经过大量研究,最相似的问题似乎是:

  • 指针算术或调用非constexpr成员: 没有指针算法,一切都是constexpr
  • 在constexpr表达式中使用reinterpret_cast: 这是使用指针,但类型是逐字的;因此,不应用任何转换
  • 使用不完整类型: 我有理由相信这种类型是完整的
这些问题中的大多数是g++不一致和发出嘎嘎声的错误。这恰恰相反

我被难住了。下面是我得到的一个精简版本,用静态断言中未在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.");

您试图将两个指向类成员的不同指针作为constexpr进行比较,如果编译器应该将其作为constexpr进行计算(对象的地址可能还不知道?),则标准中没有指定它。MSVC和clang做到了,gcc没有。指针自身的比较结果已定义。

它们不是成员。变量是静态的。你能在这个网站上找到标准吗?“这听起来有点不合理,”弗兰克·塞西利亚说,“他们仍然是静态成员。这并不等同于一个全局变量(在这种情况下可以正常工作)。如果尝试将指针与方法进行比较,也会发生同样的情况。标准没有定义在这种情况下会发生什么,所以有点ambigous@Swift全局变量模板也存在同样的问题:。指向方法的指针肯定不相似。它们几乎总是表示为强类型int。查看我在末尾添加的编辑。这与我预期的一样。@Oktalist constexpr指针变量必须由地址常量表达式初始化,即具有静态存储持续时间的对象的地址、函数的地址或空指针值。问题是,实例化的模板变量实际上是一个具有静态持续时间的对象吗(与模板的静态MEBER相同..它们不是线程范围内必需的,它们是在同一编译单元中执行任何函数或方法之前创建\初始化的,但如果-未定义,则是特定于实现的)?这在g++7.1.1中也会发生。
~$ 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.");
               ~~~~~^~~~~~~
~$ 
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");