2

Functions marked constexpr are supposed to be immutable pure functions. From the "std::max() and std::min() not constexpr" post, you can't re-channel a const-reference input as an output, since that would require the parameter to have permanence. But can you take a parameter by const-reference, as long as you don't re-channel it?

// 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() ); }

Conversely, can you return a const-reference to a sub-object of a constexpr-enabled type?

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 ]; }
};

Or do even sub-object returns have to be by value?

(Sorry if this is basic, but everything I've found dances around this point without actually being definitive.)

Community
  • 1
  • 1
CTMacUser
  • 1,996
  • 1
  • 16
  • 27

2 Answers2

4

The standard is pretty clear on what is allowed in a constexpr function:

§7.1.5 [dcl.constexpr] p3

The definition of a constexpr function shall satisfy the following constraints:

  • [...]
  • its return type shall be a literal type;
  • each of its parameter types shall be a literal type;
  • [...]

§3.9 [basic.types] p10

A type is a literal type if it is:

  • a scalar type; or
  • a reference type; or
  • a class type (Clause 9) that has all of the following properties:
  • it has a trivial destructor,
    • every constructor call and full-expression in the brace-or-equal-initializers for non-static data members (if any) is a constant expression (5.19),
    • it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
    • it has all non-static data members and base classes of literal types; or
  • an array of literal type.

As such, yes, you can have reference parameters, even reference-to-non-const ones. The parameters of a constexpr function are restricted in another way. The complete, exhaustive list can be found under §5.19 [expr.const] p2. Here's an excerpt of what makes a constexpr declared function not-so-constexpr anymore:

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 [ Note: An overloaded operator invokes a function. —end note ]:

(The last bit about the logical operators just means that the unevaluated part of it (due to short-circuit evaluation) is not part of the operations that determine whether the function is truly constexpr.)

  • [...]
  • a dynamic cast (5.2.7);
  • a reinterpret_cast (5.2.10);
  • a pseudo-destructor call (5.2.4);
  • increment or decrement operations (5.2.6, 5.3.2);
  • a typeid expression (5.2.8) whose operand is of a polymorphic class type;
  • a new-expression (5.3.4);
  • a delete-expression (5.3.5);
  • a subtraction (5.7) where both operands are pointers;
  • a relational (5.9) or equality (5.10) operator where the result is unspecified;
  • an assignment or a compound assignment (5.17); or
  • [...]
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • By that reading, `const T & std::min(const T &a, const T &b) { return !(b – Marc Mutz - mmutz Feb 22 '12 at 11:09
  • @Marc: My answer doesn't contradict Johannes' answer. It's other language rules that make `constexpr` min/max functions that return references currently impossible, as rsmith's comment on the other answer notes. – Xeo Feb 22 '12 at 11:24
  • ok, I bite the bullet: which one? You list a few, but none of them seem to ban taking or returning references. – Marc Mutz - mmutz Feb 22 '12 at 14:00
  • @Marc: I think it's adequately laid out in Johannes' answer, and I can't formulate it any better. It's the second quote, also found in §5.19/3. – Xeo Feb 22 '12 at 14:05
1

Core issue 1454's resolution changes the rule which Johannes is referencing in his answer to the std::max question. With the current resolution of that issue (which is implemented by both g++ and clang), constexpr functions are allowed to return, by reference, any lvalue which they can compute.

Community
  • 1
  • 1
Richard Smith
  • 13,696
  • 56
  • 78
  • Since this post, restrictions on `constexpr` functions have been even further relaxed (with the adoption of (your?) proposal [N3652](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html)) for the upcoming C++14 standard. Nevertheless, in the current latest draft ([N3797](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf)), 25.4.7 [alg.min.max] `min` and `max` still aren't `constexpr`; is there a chance they will be "upgraded" before the final release? – gx_ Nov 12 '13 at 15:16
  • 1
    @gx_ I don't think there's an NB comment on the C++14 draft requesting that `min` and `max` be made `constexpr`, and it's not in the LWG issues list, so it's unlikely to happen for C++14. Some members of the committee have been working on adding `constexpr` to all relevant library functions, so this should happen for C++17. – Richard Smith Nov 12 '13 at 19:26