在constexpr抛出的异常中使用副作用是否合法? [英] Is it legal to use side-effects in exceptions thrown by constexpr?

查看:168
本文介绍了在constexpr抛出的异常中使用副作用是否合法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常,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< '\\\
';
abort(); //获取核心转储
}
};

#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 )))\
:(value))

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

int main(int argc,char ** argv)
{
//编译器不能知道argc,因此必须在运行时进行求值。
//如果argc为2,则违反前提条件。
return divide(100,argc - 2);
}

我使用g ++ 4.7.2和clang ++ 3.1测试了它。当前提条件失败时,您会得到错误位置和核心转储。

  ./ constexpr_assert some_arg 
断言: ; y!= 0>失败(file:constexpr_assert.cpp,line:26)
中止(内核转储)

所以它与当前的编译器一起工作,但它是合法的C ++ 11吗?

解决方案



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


对于 constexpr 函数,如果没有函数参数值,则
函数调用替换将产生一个常量
表达式(5.19),程序是不成形的;无需诊断。


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



但是可以调用 divide(1,0)是的,它可以。调用 constexpr 函数或正常函数(§7.1.5/ 7)几乎没有区别:



< blockquote>

调用 constexpr 函数产生的结果与对
的调用结果相同non- constexpr 函数在所有方面,除了
调用 constexpr 函数可以出现在常量表达式中。


请注意,调用 constexpr 函数可以出现在常量表达式中,没有禁止他们不导致常量表达式。这是为了可以调用具有编译时和运行时参数的 constexpr 函数(否则 constexpr 的用法将是



为了完整性,让我们看看什么是一个常量表达式(§5.19/ 2):

>

一个条件表达式是一个核心常量表达式,除非它
包含以下之一作为潜在求值的子表达式$ b $ [...] [...] [...] [...] [...] [...] [...]


所以, divide(1,1) divide(1,0)不是。如果在模板参数中使用 divide(1,0),程序将会失败。但是否则很好。


Normally, constexpr must be free of side-effects. However, I just discovered that it is possible to use side-effects in the constructors of thrown exceptions. That technique can be used to emulate assert() for constexpr functions, as it is demonstrated in the following program.

#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);
}

I tested it with g++ 4.7.2 and clang++ 3.1. When the preconditions fails, you get the error location and a core dump.

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

So it works with the current compilers, but is it legal C++11?

解决方案

It is legal.

For each constexpr function there must be some argument values that result in a constant expression (§7.1.5/5):

For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19), the program is ill-formed; no diagnostic required.

Note that this does not mean that every possible argument value must result in a constant expression. divide clearly has some argument values that result in a constant expression: divide(1, 1) is a simple example. So, the definition is clearly valid.

But can divide(1, 0) be called? Yes, it can. There's almost no difference between invoking a constexpr function or a "normal" function (§7.1.5/7):

A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that a call to a constexpr function can appear in a constant expression.

Note that calls to constexpr functions can appear in constant expressions, but nothing forbids them from not resulting in constant expressions. This is intended so one can call constexpr functions with both compile-time and runtime arguments (otherwise usefulness of constexpr would be severaly limited).

For completeness, let's see what makes a constant expression (§5.19/2):

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (§3.2), but subexpressions of logical AND (§5.14), logical OR (§5.15), and conditional (§5.16) operations that are not evaluated are not considered [...].

So, divide(1, 1) is a constant expression, but divide(1, 0) is not. If you used divide(1, 0) in a template parameter, the program would be ill-formed. But otherwise it's fine.

这篇关于在constexpr抛出的异常中使用副作用是否合法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆