4

As of C++17, std::array<T,N>::begin() is constexpr:

constexpr iterator begin() noexcept;

But how can the return of begin be known at compile time? For instance:

int main() {
  auto p = std::make_unique<std::array<int,2>>();
  auto it = p->begin();
}

Is perfectly legal code (although maybe a bit useless). The start of the underlying array and thus the iterator depend on the malloc'd address.

I have the sense that I am under a misapprehension of what constexpr does as I don't see how any non-static member function could possibly be constexpr, especially if it (transitively) accesses data members.

1

3 Answers 3

7

constexpr functions can be called in non-compiletime-constant expressions. Such calls are evaluated at runtime. Only when constexpr function is called in a compiletime-constant expression, is the function evaluated at compile time.

But how can the return of begin be known at compile time?

It can be known at compile time when the array itself is a compile time constant.

The fact that it cannot be known at compile time when the array is not a compile time constant is not a problem because the function is executed at runtime in that case.

Sign up to request clarification or add additional context in comments.

3 Comments

So constexpr functions have a constexpr return value if all their arguments (including this) are constexpr? Is that fair to say?
@bitmask If they are called from a compile time constant expression. This implies that all arguments are also constant expressions because an expression cannot be constant if it has non-constant sub-expressions.
@bitmask You can only really say "the output of a constexpr function is constant only if its inputs are". If the inputs are constant expressions, then it depends on what the function is doing. E.g. for constexpr int func(int x) { return 17 / x; }, in int x; std::cin >> x; func(x);, func(x) is not a constant expression because x is not a constant expression. func(17) is a constant expression. But, func(0) is not a constant expression, because of undefined behavior. Constant expressions are defined by what they aren't, not by what they are.
2

You do not need the unique pointer to see the effect of compile time vs runtime evaluation. This does compile:

#include <array>

int main() {
  static constexpr std::array<int,2> p{1,2};
  constexpr auto it = p.begin();
}

But this does not:

#include <array>

int main() {
  std::array<int,2> p{1,2};
  constexpr auto it = p.begin();
}

Error:

<source>:5:18: error: constexpr variable 'it' must be initialized by a constant expression

  constexpr auto it = p.begin();    
                 ^    ~~~~~~~~~    
<source>:5:18: note: pointer to subobject of 'p' is not a constant expression    
<source>:4:21: note: declared here    
  std::array<int,2> p{1,2};

Sloppy speaking, the constexpr of begin() means that it is possible to evaluate the method at compile time on a constexpr object. For the non-constexpr array, the method is evaluated at runtime. Hence p.begin() cannot be used to initialize the constexpr it.

Comments

2

The problem this change solves is the ability to use a non-const std::array within a constexpr function to compute some result. Recall that constexpr functions can't call non-constexpr functions when being evaluated at compile-time.

As an example consider a function that computes the sum of i = 1 to N. And for argument's sake consider a really stupid way of doing it (non-stupid examples exist in the real world, but are more complicated than this): Create an array initialized with {1, 2, 3, ...} and then returning the sum of the elements of the array.

// Compute sum from i = 1 to N
template <unsigned N>
unsigned
sum() noexcept
{
    std::array<unsigned, N> a{};
    unsigned u = 1;
    for (auto i = a.begin(); i != a.end(); ++i, ++u)
        *i = u;
    u = 0;
    for (auto const x : a)
        u += x;
    return u;
}

This works fine and can be called like this:

auto x = sum<5>();  // x == 15

Now somebody says: Hey, let's compute this at compile time!

In C++17 this is as simple as slapping constexpr onto sum and x:

// Compute sum from i = 1 to N
template <unsigned N>
constexpr
unsigned
sum() noexcept
...

constexpr auto x = sum<5>();  // x == 15

But in C++14 this doesn't compile:

test.cpp:24:20: error: constexpr variable 'x' must be initialized by a constant expression
    constexpr auto x = sum<5>();
                   ^   ~~~~~~~~
test.cpp:11:21: note: non-constexpr function 'begin' cannot be used in a constant expression
    for (auto i = a.begin(); i != a.end(); ++i, ++u)
                    ^

And the reason it doesn't compile is because array<T, N>::begin() is not a constexpr function.

Comments

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.