"return-by-reference"或"pass-by-reference"参数何时与constexpr兼容?

When are "return-by-reference" or "pass-by-reference" parameter compatible with constexpr?

本文关键字:constexpr 兼容 参数 return-by-reference pass-by-reference 何时      更新时间:2023-10-16

标记为constexpr的函数应该是不可变的纯函数。从"std::max()and std::min()not constexpr"的帖子中,您不能将const引用输入重新作为输出,因为这需要参数具有永久性。但是你能通过const-引用获得一个参数吗,只要你不重新引导它?

// Is this still constexpr?
// (Assuming that std::complex is constexpr-safe, as it's supposed to be.)
constexpr
int  MySum( std::complex<double> const &a, std::complex<double> const &b )
{ return static_cast<int>( a.real() + b.real() ); }

相反,是否可以返回对启用constexpr类型的子对象的const引用?

template <typename T>
class MyComplex
{
    T  c_[ 2 ];
public:
    constexpr MyComplex( T r = T(), T i = T() )
    : c_{ r, i }
    {}
    // Is this actually constexpr?
    constexpr T const &  operator[]( unsigned l ) //const
    { return c_[ l ]; }
    // Can't be constexpr
    T &  operator[]( unsigned l )  { return c_[ l ]; }
};

或者,即使是子对象返回也必须按值返回吗?

(如果这是基本的,很抱歉,但我所发现的一切都围绕着这一点跳舞,但实际上并没有定论。)

标准非常清楚constexpr函数中允许的内容:

§7.1.5 [dcl.constexpr] p3

constexpr函数的定义应满足以下约束:

  • […]
  • 其返回类型应为文字类型
  • 其每个参数类型应为文字类型
  • […]

§3.9 [basic.types] p10

一个类型是文字类型,如果它是:

  • 标量类型;或
  • 引用类型
  • 具有以下所有属性的类类型(第9条):
  • 它有一个琐碎的析构函数,
    • 大括号中的每个构造函数调用和完整表达式或非静态数据的相等初始值设定项成员(如果有的话)是常数表达式(5.19)
    • 它是聚合类型(8.5.1),或者至少有一个constexpr构造函数或构造函数模板不是复制或移动构造函数,以及
    • 它具有所有非静态数据成员和文本类型的基类;或
  • 文字类型的数组

因此,是的,您可以有引用参数,甚至可以引用非常数参数。constexpr函数的参数以另一种方式受到限制。完整、详尽的列表可在§5.19 [expr.const] p2下找到。以下是使constexpr声明的函数不再是constexpr的摘录:

条件表达式核心常量表达式

(关于逻辑运算符的最后一位只是意味着它的未评估部分(由于短路评估)不是确定函数是否真的是constexpr的操作的一部分。)

  • […]
  • 动态铸造(5.2.7)
  • reinterpret_cast(5.2.10)
  • 伪析构函数调用(5.2.4)
  • 递增或递减操作(5.2.6,5.3.2)
  • typeid表达式(5.2.8),其操作数是多态类类型
  • 一个新表达式(5.3.4)
  • a删除表达式(5.3.5)
  • 减法(5.7),其中两个操作数都是指针
  • 关系运算符(5.9)或等式运算符(5.10),其中未指定结果
  • 转让或复合转让(5.17);或
  • […]

核心问题1454的解决方案改变了Johannes在回答std::max问题时引用的规则。根据该问题的当前解决方案(由g++和clang实现),constexpr函数可以通过引用返回它们可以计算的任何左值。