The standard on constexpr functions states under point 5 of [decl.constexpr]:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.
It goes on to give the following example for this:
constexpr int f(bool b){ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
What I take from this is that functions with empty argument lists are no-diagnostic ill-formed. This strikes me as extremely bizarre, such that I suspect that my understanding is incorrect. For instance, would this also be ill-formed:
constexpr int g() { return 0; } // ill-formed?
If so, what is the rationale behind this, and if not what does the qualification mean / when does a constexpr function become ill-formed?
Presumably the following are fine?
constexpr int h(int x) { return x; } // presumably fine?
constexpr int l = h(42); // also fine
The rationale for this rule is that there should be at least one context where the function can be evaluated in a constexpr context. e.g. given:
constexpr int f(bool b){ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
There is no way to invoke f() in a constexpr context, since all paths through this function will end in an expression that is not a core constant expression.
A compiler would have to evaluate all possible calls to see if there is any way the function is usable in a constexpr context. This is not easily diagnosable in general, so the language says it's ill-formed-no-diagnostic-required, i.e. you've done something wrong, but the compiler can't diagnose it.
Note that if the zero argument overload of f was the following:
constexpr int f() { return f(false); } // ok
that would be perfectly fine, since the evaluation ends in a core-constant-expression.
Similarly, this function:
constexpr int g() { return 0; } // ok
as well as this one:
constexpr int h(int x) { return x; } // ok
constexpr int l = h(42); // ok
are fine, since g and h can be invoked in a constexpr context.
The wording of "... if no argument values exist such that ..." might be confusing, as you've asked about the well-formedness of g. But g can be invoked with zero arguments, or in other words, with a void argument, so it's fine.
Related
In the following example the requires-expression of second f-function overload has the type std::integral_constant<bool,true>, which is implicitly convertible to bool:
#include <type_traits>
struct S {
static constexpr bool valid = true;
};
template<typename T>
int f() { return 1; }
template<typename T>
int f() requires( std::bool_constant< T::valid >() ) { return 2; }
int main() {
return f<S>();
}
One can observe that GCC rejects the program due to the type is not precisely bool, but Clang accepts, but selects the other overload int f() { return 1; }. Demo: https://gcc.godbolt.org/z/nf65zrxoK
Which compiler is correct here?
I believe GCC is correct—the type must be bool exactly per [temp.constr.atomic]/3 (note that E here is std::bool_constant< T::valid >()):
To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise, the lvalue-to-rvalue conversion is performed if necessary, and E shall be a constant expression of type bool. The constraint is satisfied if and only if evaluation of E results in true. If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required. [ Example:
template<typename T> concept C =
sizeof(T) == 4 && !true; // requires atomic constraints sizeof(T) == 4 and !true
template<typename T> struct S {
constexpr operator bool() const { return true; }
};
template<typename T> requires (S<T>{})
void f(T); // #1
void f(int); // #2
void g() {
f(0); // error: expression S<int>{} does not have type bool
} // while checking satisfaction of deduced arguments of #1;
// call is ill-formed even though #2 is a better match
— end example ]
You can have constexpr objects fron consteval
but you can not consume consteval within constexpr.
Why?
I thought consteval should have been some kind of "narrow" constexpr.
Please help me make a sense out of this design.
constexpr int constexpr_sqr(int n) { return n*n; }
consteval int consteval_sqr(int n) { return n*n; }
constexpr int constexpr_sqr2(int n) {
// not allowed
// return consteval_sqr(n);
// not allowed
// constexpr imm = consteval_sqr(n);
// return imm;
return constexpr_sqr(n);
}
int main() {
// while can do this
constexpr auto imm = consteval_sqr(999);
}
[LIVE]
It's the argument. constexpr function aren't required to be constant evaluated. This means that n is not usable in a constant expression.
I thought consteval should have been some kind of "narrow" constexpr.
No, those are just functions that have to be constant evaluated. This means that their arguments must always be usable in a constant expressions.
You can call a constexpr function with arguments that are not usable in a constant expression, and so long as you aren't in a context that requires a constant expression, it's still well-formed.
Consider the following code that implements a compile time counter.
#include <iostream>
template<int>
struct Flag { friend constexpr int flag(Flag); };
template<int N>
struct Writer
{
friend constexpr int flag(Flag<N>) { return 0; }
};
template<int N>
constexpr int reader(float, Flag<N>) { return N; }
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>, int value = reader(0, Flag<N + 1>{}))
{
return value;
}
template<int N = reader(0, Flag<0>{}), int = sizeof(Writer<N>) >
constexpr int next() { return N; }
int main() {
constexpr int a = next();
constexpr int b = next();
constexpr int c = next();
constexpr int d = next();
std::cout << a << b << c << d << '\n'; // 0123
}
For the second reader overload, if I put the default parameter inside the body of the function, like so:
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>)
{
return reader(0, Flag<N + 1>{});
}
Then the output will become:
0111
Why does this happen? What makes the second version not work anymore?
If it matters, I'm using Visual Studio 2015.2.
Without value being passed as a parameter, nothing stops the compiler from caching the call to reader(0, Flag<1>).
In both cases first next() call will work as expected since it will immediately result in SFINAEing to reader(float, Flag<0>).
The second next() will evaluate reader<0,0>(int, ...), which depends on reader<1>(float, ...) that can be cached if it does not depend on a value parameter.
Unfortunately (and ironically) the best source I found that confirms that constexpr calls can be cached is #MSalters comment to this question.
To check if your particular compiler caches/memoizes, consider calling
constexpr int next_c() { return next(); }
instead of next(). In my case (VS2017) the output turns into 0000.
next() is protected from caching by the fact that its default template arguments actually change with every instantiation, so it's a new separate function every time. next_c() is not a template at all, so it can be cached, and so is reader<1>(float, ...).
I do believe that this is not a bug and compiler can legitimately expect constexprs in compile-time context to be pure functions.
Instead it is this code that should be considered ill-formed - and it soon will be, as others noted.
The relevance of value is that it participates in overload resolution. Under SFINAE rules, template instantiation errors silently exclude candidates from overload resolution. But it does instantiate Flag<N+1>, which causes the overload resolution to become viable the next time (!). So in effect you're counting successful instantiations.
Why does your version behave differently? You still reference Flag<N+1>, but in the implementation of the function. This is important. With function templates, the declaration must be considered for SFINAE, but only the chosen overload is then instantiated. Your declaration is just template<int N, int = flag(Flag<N>{})> constexpr int reader(int, Flag<N>); and does not depend on Flag<N+1>.
As noted in the comments, don't count on this counter ;)
I have this very simple function which won't compile.
constexpr void func()
{
}
The error I'm getting is:
error: invalid return type 'void' of constexpr function 'constexpr void func()'
constexpr void func()
In C++14, void is a literal type [§3.9/10]:
A type is a literal type if it is:
void; or
a scalar type; or
a reference type; or
an array of literal type; or
a class type (Clause 9) that has all of the following properties:
it has a trivial destructor,
it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
all of its non-static data members and base classes are of non-volatile literal types.
Can someone explain why this is invalid?
The proposal which made void a literal type was n3652 Relaxing constraints on constexpr functions. G++ decided to push this feature to version 5 (I was using 4.9.2):
G++ now supports C++14 extended constexpr.
constexpr int f (int i)
{
int j = 0;
for (; i > 0; --i)
++j;
return j;
}
constexpr int i = f(42); // i is 42
Clang has had this implemented since version 3.4.
It is valid indeed, but not yet supported in GCC. An example in the standard actually includes constexpr functions returning void - see [dcl.constexpr]/1:
constexpr void square(int &x); // OK: declaration
// [..]
constexpr void square(int &x) { // OK: definition
x *= x;
}
Example on Coliru using Clang, which is conforming here.
Taken from The C++ Programmig Language (4th Edition):
A constexpr function may not have side-effects.
So, what would be the purpose of a constexpr void function?
If you are aiming to do something like that:
constexpr void Twice(int &a)
{
a *= 2;
}
You should consider changing to:
constexpr int Twice(int a)
{
return 2 * a;
}
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?
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).