Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

non-constexpr calls in constexpr functions

This is a simplified example from code meant to generate sequences of arbitrary values (in the sense of std::iota) and iterators of varying categories over them:

struct delta
{
    template<typename I>
    void inc(I& i) { ++i; }

    template<typename I>
    I next(I i) { inc(i); return i; }
};

There are many classes like delta, each defining inc differently, e.g. --i, i += step, i -= step, i *= step, f(i) etc. Function next remains the same and is actually shared in a base class.

We are generating the value-based operation of next from the mutating operation of inc. Doing the opposite would be equivalent, however we choose this design for performance, because next is only expected to be called at some initialization, while inc may be called a million times.

The problem is that in case some parameters are compile-time constant, I would like to call next at compile-time given a constexpr argument i.

This is not possible in C++11 because of the call to non-constexpr function inc. Simply changing to

template<typename I>
constexpr I next(I i) { inc(i); return i; }

or

template<typename I>
constexpr I next(I i) { return inc(i), i; }

will not work. Of course, we could provide another special function like

template<typename I>
constexpr I next(I i) { return i + 1; }

but this is too much code duplication, given that there are many classes like delta and many other operations like inc/next.

I have seen that constexpr function restrictions are to be relaxed in C++14, but I cannot achieve this in practice yet.

So:

  • will this work in C++14 eventually?
  • what is the status of standardization?
  • what is the status of compilers?
  • any possible workaround?

EDIT

It seems inc should be constexpr as well (though void). This works on clang 3.4:

struct delta
{
    template<typename I>
    constexpr void inc(I& i) { ++i; }

    template<typename I>
    constexpr I next(I i) { inc(i); return i; }
};

...but not on gcc 4.8.2. So is the code above correct C++14? Is it only a matter of time for gcc?

like image 811
iavr Avatar asked May 03 '14 10:05

iavr


People also ask

Can constexpr functions call non constexpr functions?

A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.

Can a constexpr function throw?

Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.

Should I use constexpr or const?

const applies for variables, and prevents them from being modified in your code. constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc.


Video Answer


1 Answers

It is not surprising that this example does not work on gcc, according to this page C++14s generalized constexpr functions are not yet supported.

I believe the source code in your edit is valid C++14, the Draft Standard available here contains, on page 126(§5.19) an example that is very similiar to yours(without template, non member functions and they use a temporary):

constexpr int incr(int &n) {
    return ++n;
}

constexpr int g(int k) {
    constexpr int x = incr(k);// error: incr(k) is not a core constant
                            // expression because lifetime of k
                            // began outside the expression incr(k)
    return x;
}

constexpr int h(int k) {
    int x = incr(k);
    // OK: incr(k) is not required to be a core
    // constant expression
    return x;
}

constexpr int y = h(1); // OK: initializes y with the value 2
// h(1) is a core constant expression because
// the lifetime of k begins inside h(1)

If my reading of the Standard is correct, the fact that yours are member functions should not matter, because "this" is not evaluated and it seems like the code does not violate any of the other rules outlaid in §5.19(2).

like image 79
Philipp Lenk Avatar answered Sep 21 '22 15:09

Philipp Lenk