Каково использование constexpr для функции non-const member?

Принятый ответ в литеральной ошибке компиляции класса с конструктором и функцией constexpr (отличается vc, g++) показывает, что в С++ 14 существует разница в том, как constexpr int A::a() и constexpr A::a() const. т.е. constexpr в функции-члене не означает, что функция не изменяет объект, на котором он действует.

Данный пример:

struct A {
    constexpr A() {}
    constexpr int a() {return 12; }
    constexpr int b() const {return 12; }
};

int main()
{
    constexpr A a;
    // DOES NOT COMPILE as a() is not const
    // constexpr int j = a.a();
    const int k = a.b(); // Fine since b() is const
}

Мне constexpr на a() кажется бесполезным. Существует ли конкретное использование для constexpr для constexpr [const11] non- const?

Ответ 1

Вопрос: как вы создаете массив constexpr размером 1024 когда все элементы установлены в 0 кроме элемента 42 элемента, который должен быть 11?

#include <array>

constexpr auto make_my_array() -> std::array<int, 1024>
{
    std::array<int, 1024> a{};
    a[42] = 11; // std::array::operator[] is constexpr non-const method since C++17

    return a;
}

auto test()
{
    constexpr std::array<int, 1024> a = make_my_array();
}

Или лучшее предложение от @michael-anderson make_iota_array:

template <std::size_t N>
constexpr auto make_iota_array() -> std::array<int, N>
{
    std::array<int, N> a{};

    for (std::size_t i = 0; i < N; ++i)
        a[i] = i;

    return a;
}

Ответ 2

constexpr означает, что "может использоваться, когда требуется постоянное выражение". "Подразумеваемая константа" для объявленных объектов не означает, что мы не можем иметь неконстантные объекты в других контекстах. Например, несколько надуманный пример, созданный из вашего собственного:

template<int>
struct foo {
};

struct A {
    int i = 0;
    constexpr A() {}
    constexpr int a() { return i; }
    constexpr int b() const {return 12; }
    constexpr A&  c() { ++i; return *this; }
};

int main()
{
    foo<A{}.c().a()> f1;
}

Очевидно, что аргумент шаблона должен быть постоянным выражением. Теперь A{} является prvalue литерального типа с constexpr c'tor, а это неконстантный объект. Функция-член имеет право изменять эту "константу", потому что все вычисление может свернуть к постоянному выражению во время компиляции. Это обоснование правил, на одной ноге.