Constexpr уникальный id, компилируется с clang, но не с gcc

Утро, ребята!

Я реорганизую очередь событий. Я пытаюсь посмотреть, могу ли я сделать идентификаторы событий уникальными во время компиляции. То, что я придумал, работает с clang 4.0.0, но дает компиляцию с g++ 6.3.1.

Идея состоит в том, чтобы использовать адрес статической членной переменной для уникальной идентификации отдельных типов, а затем использовать тегирование для генерации этих уникальных типов из шаблона класса.

Использование адреса статического члена как идентификатора типа является достаточно распространенным методом, но использование шаблонов для этого означает отказ от ODR. MSN цитирует стандарт здесь, чтобы предположить, что это действительный подход: Идентификатор константы времени компиляции

Моя проблема делает это constexpr. Если я удалю constexpr и протестирую это во время выполнения, все будет работать так, как ожидалось. Однако выполнение этого constexpr не дает статического утверждения в g++, говорящего: "ошибка: непостоянное условие для статического утверждения".

После исследования довольно много, кажется, наиболее похожие проблемы:

Большинство из этих проблем - это g++, являющийся несоответствующим и clang++ ошибкой. Это наоборот.

Я в тупике. Здесь урезанная версия того, что у меня есть, аннотированная тем, что не компилируется в g++ в static, утверждает:

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.

(Я не спрашиваю, является ли это хорошим дизайном или существуют другие способы для этого. У меня есть другие способы сделать это.)

Спасибо!

EDIT: сопоставимый пример без шаблонов, который компилируется с обоими компиляторами, может быть:

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

Ответ 1

Вы пытаетесь сравнить два разных указателя с членами класса как constexpr, и он не указан в стандарте, если компилятор должен оценивать его как constexpr (адреса объектов еще не известны?). MSVC и clang делают это, gcc нет. Определен результат сравнения указателя с самим собой.