10
votes

Morning, folks!

I'm refactoring an event queue. I'm poking around to see if I can make event ids unique at compile time. What I've come up with works with clang 4.0.0, but gives a compile error with g++ 6.3.1.

The idea is to use the address of a static member variable to uniquely identify individual types, then use tagging to generate these unique types from a class template.

Using the address of a static member as a type id is a reasonably common technique, but using templates to do it means being clear of the ODR. MSN cites the standard here to suggest that this is a valid approach: Compile-time constant id

My problem is doing this constexpr. If I remove constexpr and test this at runtime, everything works as expected. Doing this constexpr, however, fails a static assert in g++ saying, "error: non-constant condition for static assertion".

After researching quite a bit, it seems the most similar problems are:

Most of these problems are g++ being nonconforming and clang++ erroring out. This is the opposite.

I'm stumped. Here's a stripped-down version of what I've got, annotated with what doesn't compile in g++ in the static asserts:

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

And here are the compiler outputs:

~$ 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.");
               ~~~~~^~~~~~~
~$ 

I'm curious why this specific approach doesn't compile with gcc, but does with clang, because this conflicts with how I understand constexpr.

(I'm not asking if this is a good design, or if there are other ways to accomplish this. I have other ways to accomplish this.)

Thanks!

EDIT: A comparable example without templates that does compile with both compilers might be:

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
This also happens with g++ 7.1.1.Frank Secilia

1 Answers

1
votes

You're trying to compare two distinct pointers to class members as constexpr and it isn't specified in standard, if compiler should evaluate it as constexpr (addresses of objects might not be yet known?). MSVC and clang does that, gcc doesn't. The result for comparision of pointer to itself is defined.