constexpr唯一ID,用clang编译但不用gcc编译

Fra*_*lia 10 c++ gcc constexpr c++11

早上好,伙计们!

我正在重构一个事件队列.我正在四处寻找,看看我是否可以在编译时使事件ID独特.我提出的与clang 4.0.0一起工作,但是给出了g ++ 6.3.1的编译错误.

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

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

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

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

大多数这些问题都是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.");
Run Code Online (Sandbox Code Playgroud)

以下是编译器输出:

~$ 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.");
               ~~~~~^~~~~~~
~$ 
Run Code Online (Sandbox Code Playgroud)

我很好奇为什么这个特定的方法不能用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");
Run Code Online (Sandbox Code Playgroud)

Swi*_*Pie 1

您正在尝试将两个不同的指向类成员的指针作为 constexpr 进行比较,并且标准中未指定它,如果编译器应将其评估为 constexpr (对象的地址可能还不知道?)。MSVC 和 clang 可以做到这一点,而 gcc 则不能。定义了指针与其自身的比较结果。