6

In C++14 and beyond, constexpr for member functions no longer implies const.

struct Value
{
    int i = 5;

    constexpr bool not_five() // requires const to compile
    {
        return this->i != 5;
    }
};

int main()
{
    constexpr Value v{6};
    static_assert(v.not_five());
}
error: passing ‘const Value’ as ‘this’ argument discards qualifiers [-fpermissive]
  static_assert(v.not_five());
                           ^

It seems as though calling a non-const constexpr member function at compile time implies the mutation of a constant, since the object it's called against exists at compile time and is being mutated. Under what circumstances is the concept of a non-const constexpr member function useful?

3
  • Does this answer your question? stackoverflow.com/questions/50149036/… ? Commented Dec 5, 2019 at 0:44
  • @KamilCuk Yeah I just noticed that one, not sure how I missed it earlier. The accepted answer there doesn't make much sense to me, but the other one provides a prvalue based example. Would it be correct to say that prvalues can be mutated at compile time, but once assigned become constant? Commented Dec 5, 2019 at 0:49
  • @KamilCuk After thinking about the accepted answer some more - would it be correct to say that, within a constexpr function, you might wish to create and mutate an object instance before returning it. However, you won't be able to call a non-constexpr function at compile time, so you'll need a non-const constexpr one in that scenario. Commented Dec 5, 2019 at 1:00

1 Answer 1

2

It seems that I inadvertently duplicated this question as well as this other one. Based on their answers, there are (at least) two concrete scenarios in which a non-const constexpr member function is useful.

  1. Mutation of temporaries. It is permissible to mutate a temporary value at compile time; it only becomes constant upon assignment.

  2. Mutation of objects during function execution. While executing, a constexpr function might create an object instance and then mutate it via member functions. Those member functions can't be called at compile time unless they're constexpr, but they can't mutate the instance they're called against if they're const.

struct Value
{
    int i{};

    constexpr Value add(int n)
    {
        this->i += n;
        return *this;
    }

    constexpr Value add(Value other) const
    {
        Value ret{this->i};
        ret.add(other.i); // call to non-const constexpr member
        return ret;
    }
};

int main()
{
    constexpr Value v = Value{}.add(1);
    // v.add(1); // illegal
    constexpr Value u = v.add(v);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Technically there's no object that "becomes" constexpr; every variable either is or isn't. (There's also no assignment happening here, just initialization which uses the = token.) In constexpr Value v = Value{}.add(1); there are two different Value objects: the temporary materialized from Value{} which is not const, so add(int) can be called, then the object v which is initialized from the *this expression in the return statement, so v is copy-initialized from the temporary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.