constexpr decltype

Constexpr decltype

我最近在这里 (Detecting instance method constexpr with SFINAE) 问了一个问题,我试图在编译时做一些 constexpr 检测。最终,我发现可以利用 noexcept 来做到这一点:任何常量表达式也是 noexcept。所以我组装了以下机器:

template <class T>
constexpr int maybe_noexcept(T && t) { return 0; }
...
constexpr bool b = noexcept(maybe_noexcept(int{}));

这行得通并且 b 正如您所期望的那样为真,因为零初始化 int 是一个常量表达式。它也应该正确地产生零(如果我将 int 更改为其他适当的类型)。

接下来,我想检查某些东西是否 constexpr 可移动构造。所以我这样做了:

constexpr bool b = noexcept(maybe_noexcept(int(int{})));

同样,这适用于 int 或用户定义的类型。但是,这会检查该类型是否同时具有 constexpr 默认构造函数和 constexpr 移动构造函数。因此,为了解决这个问题,我尝试更改为 declval:

constexpr bool b = noexcept(maybe_noexcept(int(declval<int>())));

这导致 b 在 gcc 5.3.0 中为 false(不能使用 clang 来处理这些,因为 clang 不能正确地生成常量表达式 noexcept)。没问题,我说,一定是因为 declval(很有趣)没有标记为 constexpr。所以我写了我自己的幼稚版本:

template <class T>
constexpr T&& constexpr_declval() noexcept;

是的,与标准库的处理方式相比,这是幼稚的,因为它会因 void 和其他可能的原因而窒息,但目前还好。所以我再试一次:

constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>())));

这还是不行,b总是假的。为什么这不被视为常量表达式?这是编译器错误,还是我不了解 constexpr 的基础知识? constexpr 和未评估的上下文之间似乎存在一些奇怪的相互作用。

constexpr 表达式必须定义。你的没有定义,所以在那种情况下 int(constexpr_declval<int>()) 不是 constexpr.

这意味着 maybe_noexcept(int(constexpr_declval<int>())) 不是 constexpr,因此也不是 noexcept

并且编译正常returnsfalse.

您也不能在 constexpr 中调用 UB。

我想不出对任意数据进行 constexpr 引用的方法。我在考虑将对齐存储的 constexpr 缓冲区重新解释为对数据类型的引用,但在许多情况下这是 UB,因此不是-constexpr.

一般来说,这是不可能的。假设您有一个 class,其状态决定方法调用是否为 constexpr:

struct bob {
  int alice;
  constexpr bob(int a=0):alice(a) {}
  constexpr int get() const {
    if (alice > 0) throw std::string("nope");
    return alice;
  }
};

现在,是bob::get constexpr还是不是?如果你有一个 constexpr bob 用非正数 alice 构造的,并且......如果不是,则不是。

你不能说 "pretend this value is constexpr and tell me if some expression is constexpr"。即使可以,它也不能解决一般问题,因为 constexpr 参数的 state 可以根据表达式是否为 constexpr 而改变!

更有趣的是,bob().get() 是 constexpr,而 bob(1).get() 不是。所以你的第一次尝试(默认构造类型)甚至给出了错误的答案:你可以测试,然后执行操作,操作将失败。

对象实际上是方法的参数,没有所有参数的状态,您无法确定函数是否 constexpr

确定表达式是否为 constexpr 的方法是 运行 在 constexpr 上下文中查看它是否有效。