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:
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?
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.
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.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With