【问题标题】:Is it legal to use side-effects in exceptions thrown by constexpr?在 constexpr 抛出的异常中使用副作用是否合法?
【发布时间】:2012-10-13 21:20:31
【问题描述】:

通常,constexpr 必须没有副作用。但是,我刚刚发现可以在抛出异常的构造函数中使用副作用。该技术可用于模拟 constexpr 函数的 assert(),如下面的程序所示。

#include <iostream>
#include <cstdlib>
#include <stdexcept>

struct constexpr_precond_violated : std::logic_error
{
  constexpr_precond_violated(const char* msg) :
    std::logic_error(msg)
  {
    std::cerr << msg << '\n';
    abort(); // to get a core dump
  }
};

#define TO_STRING_IMPL(x) #x
#define TO_STRING(x) TO_STRING_IMPL(x)

#define CONSTEXPR_PRECOND(cond, value) \
  ((!(cond)) ? throw constexpr_precond_violated( \
    "assertion: <" #cond "> failed (file: " \
    __FILE__ ", line: " TO_STRING(__LINE__) ")")    \
   : (value))

constexpr int divide(int x, int y)
{
  return CONSTEXPR_PRECOND(y != 0, x / y);
}

int main(int argc, char** argv)
{
  // The compiler cannot know argc, so it must be evaluated at runtime.
  // If argc is 2, the precondition is violated.
  return divide(100, argc - 2);
}

我使用 g++ 4.7.2 和 clang++ 3.1 对其进行了测试。当先决条件失败时,您将获得错误位置和核心转储。

./constexpr_assert some_arg
assertion: <y != 0> failed (file: constexpr_assert.cpp, line: 26)
Aborted (core dumped)

所以它适用于当前的编译器,但它是合法的 C++11 吗?

【问题讨论】:

  • 出于兴趣,如果您将编译时常量0 作为第二个参数提供给divide(),会发生什么? 编译器是否“抛出异常”? :)
  • static_assert(divide(1, 0) >= 0, "testing");根本不会编译 return divide(1, 0);使用 Clang 编译并且仅在运行时失败。
  • 您必须小心区分常量表达式和constexpr 函数。像“constexpr 必须没有副作用”这样的东西是不精确的。将constexpr 视为一个关键字(想到static)。
  • 您可以在&amp;&amp;||?: 的子表达式上使用在编译时不计算的表达式。异常的构造函数没有什么特别之处。

标签: c++ c++11 assert language-lawyer constexpr


【解决方案1】:

这是合法的。

对于每个constexpr 函数,必须有一些导致常量表达式的参数值(第 7.1.5/5 节):

对于constexpr 函数,如果不存在这样的函数参数值 函数调用替换将产生一个常量 表达式 (5.19),程序格式错误;无需诊断。

请注意,这并不意味着每个可能的参数值都必须产生一个常量表达式。 divide 显然有一些导致常量表达式的参数值:divide(1, 1) 是一个简单的例子。所以,这个定义显然是有效的。

但是可以调用divide(1, 0) 吗?是的,它可以。调用 constexpr 函数或“普通”函数(第 7.1.5/7 节)几乎没有区别:

调用constexpr 函数产生与调用相同的结果 一个等价的非constexpr 函数,除了一个 对constexpr 函数的调用可以出现在常量表达式中。

请注意,对constexpr 函数的调用可以出现在常量表达式中,但没有什么能阻止它们不产生常量表达式。这是为了让人们可以使用编译时和运行时参数调用constexpr 函数(否则constexpr 的用处将受到限制)。

为了完整起见,让我们看看什么是常量表达式(第 5.19/2 节):

一个条件表达式是一个核心常量表达式,除非它 涉及以下之一作为潜在评估的子表达式 (第 3.2 节),但逻辑与(第 5.14 节)、逻辑或(第 5.15 节)的子表达式, 和未评估的条件(§5.16)操作不是 考虑 [...]。

所以,divide(1, 1) 是一个常量表达式,但 divide(1, 0) 不是。如果您在模板参数中使用divide(1, 0),则程序格式错误。但其他都很好。

【讨论】:

    猜你喜欢
    • 2016-05-13
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 2019-05-12
    相关资源
    最近更新 更多