2

assume we have const array:

const int g_Values[] = { ... };

how check that members grow monotonically at compile time, i.e. g_Values[i] < g_Values[i + 1]

in runtime this possible to check like this:

bool IsMonotonously()
{
    int i = _countof(g_Values);
    int m = MAXINT;
    do 
    {
        int v = g_Values[--i];
        if (v >= m) return false;
        m = v;
    } while (i);
    return true;
}

but how rewrite this with constexpr and if IsMonotonously() return false - generate compile time error.

cigien
  • 57,834
  • 11
  • 73
  • 112
lomo
  • 23
  • 3

2 Answers2

5

This is impossible for an array that is just const. You need to make it constexpr to be able to use it in a constexpr context.

All you need to do in addition to this is to implement the function for checking the array as constexpr:

template<class T, size_t N>
constexpr bool IsStrictlyMonotonouslyIncreasing(T (&arr)[N])
{
    bool result = true;

    if (N > 1)
    {
        for (size_t i = 0; result && (i != N - 1); ++i)
        {
            result = (arr[i] < arr[i + 1]);
        }
    }

    return result;
}

const int g_Values[] = { 1, 2, 3, 4 };
static_assert(IsStrictlyMonotonouslyIncreasing(g_Values)); // compiler error g_Values is not usable in a constexpr context

constexpr int g_Values2[] = { 1, 2, 3, 4 };
static_assert(IsStrictlyMonotonouslyIncreasing(g_Values2)); // ok
fabian
  • 80,457
  • 12
  • 86
  • 114
  • @ArminMontigny Can you point me to the compiler where this fails? Note: I'm assuming c++20 and gcc, clang and msvc seem to accept it: https://godbolt.org/z/6jExqPG4T also the loop breaks the first time we identify 2 neighboring indices that are different, so no need for `&=`, check the condition in the `for` loop again. – fabian Oct 15 '22 at 10:02
1

Fantastic answer is given already. Just as an additional note:

Starting with C++20, several functions in the algorithm library have now also a constexpr implementation. This includes std::adjacent_find. And this also in the "ranges" version.

The solution to your problem is nearly given in the cpp reference here with using std::ranges::adjacent_find.

The example uses std::ranges::greater as the predicate which would allow for repeating values, resulting in none-strict ordering.

So, we need to use std::ranges::greater_equal. This will also deduce the parameter types of the function call operator from the arguments automatically, which makes live a little bit easier.

The code could look something like the below:

#include <iostream>
#include <cassert>
#include <algorithm>
#include <iterator>
#include <functional>
namespace rng = std::ranges;

template <typename T, size_t N>
constexpr bool increasingValues(T(&a)[N]) noexcept {
    return (rng::end(a) == rng::adjacent_find(a,  rng::greater_equal()));
}
constexpr int arr[] = { 10, 20, 20, 40, 50};

static_assert(increasingValues(arr), "Array values are not increasing");

int main() {
    std::cout << *(std::end(arr) - 1)<< '\n';
}

With Microsoft Visual Studio, the editor would already show the problem.

And the compiler would generate 2 messages with a clear hint.

Static assertion failed with "Array values are not increasing" 

Please see a screen shot below:

enter image description here




.

.

.

By the way, the accepted answer compiles like the below

enter image description here

And with fixing to constexpr, we can see:

enter image description here

cigien
  • 57,834
  • 11
  • 73
  • 112
A M
  • 14,694
  • 5
  • 19
  • 44
  • Could you add the error messages from the first screenshot as text? The red squiggly line makes it clear enough where the error comes from, but the actual text of the message is hard to read. – cigien Oct 17 '22 at 04:40
  • Thanks. I actually meant for it to be added to the answer itself so that future readers could see it, not just for myself. I've added it in now. – cigien Oct 17 '22 at 13:15