【问题标题】:Constexpr decltypeconstexpr decltype
【发布时间】:2016-02-08 18:22:00
【问题描述】:

我最近在这里问了一个问题 (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 中为假(不能使用 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 和未评估的上下文之间似乎存在一些奇怪的交互。

【问题讨论】:

  • @Cameron 您所说的第二部分当然是正确的,但从技术上讲,第一部分不是。每个常量表达式 noexcept,它可能不是。但是,constexpr 函数的返回并不总是一个常量表达式。
  • 你的函数调用不是常量表达式,因为它是未定义的 ([expr.const]/2.3)

标签: c++ c++11 template-meta-programming constexpr


【解决方案1】:

constexpr 必须定义表达式。你的没有定义,所以在这种情况下int(constexpr_declval&lt;int&gt;()) 不是constexpr

这意味着maybe_noexcept(int(constexpr_declval&lt;int&gt;())) 不是constexpr,所以也不是noexcept

并且编译器正确返回false

您也不能在 constexpr 中调用 UB。

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

一般来说,这是不可能的。假设你有一个类,它的状态决定方法调用是否为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 构造的,并且......如果没有,那就不是。

你不能说“假设这个值是constexpr,然后告诉我某个表达式是否是constexpr”。即使可以,它也不能解决一般问题,因为constexpr 参数的状态 可以改变,如果表达式是constexpr 或不是!

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

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

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

【讨论】:

  • 所以似乎实际上不可能正确测试一个类是否存在 constexpr 实例方法,因为您需要获取一个实例来执行此操作,而没有办法获取 constexpr 实例这既是实际定义的,也不假定存在某些构造函数。这是对情况的公平描述吗?
  • @NirFriedman 好吧,您可以传入参数来构造所述对象。 ;) 一般来说,constexpr 是上下文相关的——您需要了解关于调用及其参数的几乎一切。其中一个参数是对象进行调用:如果不是constexpr,则表达式(通常)不是。据我所知,不支持说“如果假装这个论点是constexpr,即使它不是发生的事情”。
  • 确实哈哈。好的,您的回答+评论非常有用,谢谢。
  • @NirFriedman 我添加了一个明确的具体示例,说明为什么您所问的问题在问题中实际上是不可能的。
  • 哇,谢谢你的例子,非常有见地。这绝对是值得深思的。我想我开始更好地理解为什么 constexpr 具有这种奇怪的状态,它不是像 const 这样的类型系统中的第一类。我仍然希望他们能够有条件地使事情成为可能,就像使用 noexcept(...) 一样。我可以忍受更糟糕的检测(在实践中很少有所作为),但我需要使用我的检测器编写 SFINAE 重载......似乎很痛苦且没有必要。除非出于某种原因 constexpr(compile_time_boolean) 不起作用...
猜你喜欢
  • 2017-03-28
  • 2019-08-10
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 2014-02-24
  • 2015-05-04
  • 2023-03-15
  • 2016-07-24
相关资源
最近更新 更多