【问题标题】:constexpr unique id, compiles with clang but not with gccconstexpr 唯一 id,使用 clang 编译,但不使用 gcc
【发布时间】:2017-10-12 19:47:21
【问题描述】:

早安,伙计们!

我正在重构一个事件队列。我正在四处寻找是否可以在编译时使事件 id 唯一。我提出的适用于 clang 4.0.0,但使用 g++ 6.3.1 时会出现编译错误。

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

使用静态成员的地址作为类型 id 是一种相当常见的技术,但使用模板来做到这一点意味着要清除 ODR。 MSN 在这里引用标准表明这是一种有效的方法: Compile-time constant 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.");

这里是编译器的输出:

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

【问题讨论】:

  • g++ 7.1.1 也会发生这种情况。

标签: c++ c++11 gcc constexpr


【解决方案1】:

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

【讨论】:

  • 他们不是会员。变量是静态的。你能把标准放在这上面吗?这听起来有点不合理。
  • @Frank Secilia 他们仍然是静态成员。这不等同于全局变量(在这种情况下可以正常工作)。如果您尝试比较指向方法的指针,也会发生同样的情况。标准没有定义在这种情况下会发生什么,所以有点模棱两可
  • @Swift 与全局变量模板相同的问题:demo.
  • 指向方法的指针绝对不相似。它们几乎总是表示为强类型整数。查看我最后添加的编辑。就像我预期的那样工作。
  • @Oktalist constexpr 指针变量必须由地址常量表达式初始化,即具有静态存储持续时间的对象的地址、函数的地址或空指针值。问题是,实例化的模板变量实际上是一个具有静态持续时间的对象吗,(与模板的静态成员相同。它们不是线程范围的,它们是在执行任何同一编译单元中的函数或方法,但是当 - 未定义时,它是特定于实现的)?
猜你喜欢
  • 2022-10-18
  • 1970-01-01
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
相关资源
最近更新 更多