constexpr void function rejected - c++

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;
}

Related

What does [decl.constexpr].5 mean exactly?

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.

Constexpr member function

Suppose I have a struct template S that is parametrized by an engine:
template<class Engine> struct S;
I have two engines: a "static" one with a constexpr member function size(), and a "dynamic" one with a non-constexpr member function size():
struct Static_engine {
static constexpr std::size_t size() {
return 11;
}
};
struct Dynamic_engine {
std::size_t size() const {
return size_;
}
std::size_t size_ = 22;
};
I want to define size() member function in S that can be used as a constexpr if the engine's size() is constexpr. I write:
template<class Engine>
struct S {
constexpr std::size_t size() const {
return engine_.size();
}
Engine engine_;
};
Then the following code compiles with GCC, Clang, MSVC and ICC:
S<Static_engine> sta; // not constexpr
S<Dynamic_engine> dyn;
constexpr auto size_sta = sta.size();
const auto size_dyn = dyn.size();
Taking into account intricacies of constexpr and various "ill-formed, no diagnostic is required", I still have the question: is this code well-formed?
Full code on Godbolt.org
(I tagged this question with both c++17 and c++20 in case this code has different validity in these two standards.)
The code is fine as written.
[dcl.constexpr]
6 If the instantiated template specialization of a constexpr
function template or member function of a class template would fail to
satisfy the requirements for a constexpr function or constexpr
constructor, that specialization is still a constexpr function or
constexpr constructor, even though a call to such a function cannot
appear in a constant expression. If no specialization of the template
would satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed, no diagnostic required.
The member may not appear in a constant expression for the specialization that uses Dynamic_engine, but as the paragraph above details, that does not make S::size ill-formed. We are also far from ill-formed NDR territory, since valid instantations are possible. Static_engine being a prime example.
The quote is from n4659, the last C++17 standard draft, and similar wording appears in the latest C++20 draft.
As for the evaluation of sta.size() as a constant expression, going over the list at [expr.const] I cannot find anything that is disallowed in the evaluation itself. It is therefore a valid constant expression (because the list tells us what isn't valid). And in general for a constexpr function to be valid, there just needs to exist some set of arguments for which the evaluation produces a valid constant expression. As the following example form the standard illustrates:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
struct B {
constexpr B(int x) : i(0) { } // x is unused
int i;
};
int global;
struct D : B {
constexpr D() : B(global) { } // ill-formed, no diagnostic required
// lvalue-to-rvalue conversion on non-constant global
};
Yes.
Functions may be marked as constexpr without being forced to be evaluated at compile-time. So long as you satisfy the other requirements for marking a function as constexpr, things are okay (returns a literal type, parameters are literals, no inline asm, etc.). The only time you may run into issues is if it's not actually possible to create arguments that satisfy the function being called as a core constant expression. (e.g., if your function had undefined behavior for all values, then your function would be ill-formed NDR)
In C++20, we received the consteval specifier that forces all calls to the function to be able to produce a compile-time constant (constexpr).
Not a direct answer but an alternative way:
struct Dynamic_Engine
{
using size_type = size_t;
size_type size() const
{
return _size;
}
size_type _size = 22;
};
struct Static_Engine
{
using size_type = std::integral_constant<size_t, 11>;
size_type size() const
{
return size_type();
}
};
template <typename ENGINE>
struct S
{
auto size() const
{
return _engine.size();
}
ENGINE _engine;
};
int main()
{
S<Static_Engine> sta;
S<Dynamic_Engine> dyn;
const auto size_sta = sta.size();
const auto size_dyn = dyn.size();
static_assert(size_sta == 11);
}
I had the same kind of problems and IMHO the easiest and more versatile solution is to use std::integral_constant. Not more needs to juggle with constexpr as the size information is directly encoded into the type
If you still really want to use constexpr (with its extra complications) you can do:
struct Dynamic_Engine
{
size_t size() const
{
return _size;
}
size_t _size = 22;
};
struct Static_Engine
{
static constexpr size_t size() // note: static
{
return 11;
}
};
template <typename ENGINE>
struct S
{
constexpr size_t size() const
{
return _engine.size();
}
ENGINE _engine;
};
int main()
{
S<Static_Engine> sta;
S<Dynamic_Engine> dyn;
constexpr size_t size_sta = sta.size();
const size_t size_dyn = dyn.size();
static_assert(size_sta == 11);
}

constexpr operator overloading issues with using arguments

I am making a simple class inheriting from std::array. The point is that it should throw a compile time error if the subscript operator is used for an out of bounds index. However, I keep getting an error message. This is the code simplified.
#include <array>
using namespace std;
template<typename type, size_t size>
struct container : array<type,size>
{
constexpr inline type& operator[](int index) const
{
static_assert(index<size,"");
return ((static_cast<const array<type,size> >(*this))[index]);
}
template<class... bracelist>
constexpr container(bracelist&&... B)
:array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[2];
}
The error it gives me is:
main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression
However, I used "index" in the return statement, and commenting out the static_assert makes it work fine. If index was not a constant expression, wouldn't I not be able to use it in the subscript operator for std::array after the static_cast? I am new to using the constexpr functionality, so any help would be appreciated. Thank you.
Note: I am aware std::array's constexpr subscript operator already does this, I just want to know how to do this for future uses. Thanks.
There are 2 really useful features of constexpr functions, the interplay of which is not always fully appreciated.
In constexpr context they only evaluate code paths that are taken for the constexpr arguments.
In non-constexpr context they behave exactly like regular functions.
Which means that we can use exceptions to great effect.
Since while in constexpr context, if the exception path is taken, this is a compiler error (throw is not allowed in constexpr context). You get to see the "exception" in your compiler's error output.
example:
#include <array>
#include <stdexcept>
template<typename type, std::size_t size>
struct container : std::array<type,size>
{
constexpr auto operator[](std::size_t index) const
-> type const&
{
if (index < size)
return static_cast<const std::array<type,size>>(*this)[index];
else
throw std::out_of_range("index out of range" + std::to_string(index));
}
template<class... bracelist>
constexpr container(bracelist&&... B)
: std::array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[4];
}
Example output:
main.cpp: In function 'int main()':
main.cpp:28:37: in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
throw std::out_of_range("index out of range" + std::to_string(index));
This approach is actually more versatile than static_assert, since it works at both compile and runtime.
The thing you have to keep in mind is that constexpr functions can be called at runtime with non constexpr arguments. constexpr means for a function that the function is usable in a compile-time evaluated expression (e.g. another constexpr or a template argument) but not exclusively. A constexpr function can still be called in the classical way, i.e. at run-time with run-time variables. Which means that the parameters of a constexpr function cannot be and are not compile-time constants.
It doesn't apply to your case but in general if you know a parameter will always be called with a compile time constant than you can make it a template parameter.
constexpr void foo(int a)
{
static_assert(a != 0); // illegal code because the parameter
// cannot be a compile time constant
}
void test()
{
int x;
std::cin >> x;
foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
static_assert(N != 0); // legal
}
void test()
{
int x;
std::cin >> x;
foo<x>(); // illegal, x is not a compile time constant
foo<24>(); // legal
constexpr int c = 11;
foo<c>();; // legal
}
This is the reason for std::get<N>(array) — it is the only way to assuredly pass a "compile-time value" in a manner that'll satisfy the rules of the language. Your attempt to create a compile-time op[] cannot work. You could of course make your own templated accessor like std::get, but one might ask why not just use std::array as it is already.

constexpr expression and variable lifetime, an example where g++ and clang disagree

Consider the simple C++11 code:
template<int N>
struct Foo {};
template <int N>
constexpr int size(const Foo<N>&) { return N; }
template <int N>
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
int main()
{
Foo<5> foo;
constexpr int x = size(foo); // works with gcc and clang
// _but_
use_size(foo); // the same statement in the use_size()
// function _only_ works for gcc
}
I can successfuly compile it with g++ -std=c++11 foo.cpp
however if I use clang++, clang++ -std=c++11 foo.cpp I get
foo.cpp:15:28: error: constexpr variable 'n' must be initialized by a constant expression
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
~~~~~^~~~
foo.cpp:23:5: note: in instantiation of function template specialization 'use_size<5>' requested here
use_size(foo); // the same statement in the use_size()
^
1 error generated.
(nb: compiler versions. I have checked the previous statement with g++ version 5.3.1 and 7.2.1 and with clang++ version 3.6.2 and 5.0.0)
My question: which of g++ or clang is right? What is the problem?
My interpretation is that clang++ is right and g++ is too permissive.
We can find a close example ([expr.const] section, page 126) in the standard https://isocpp.org/std/the-standard (draft can be downloaded, attention big PDF! ).
constexpr int g(int k) {
constexpr int x = incr(k);
return x;
}
where it is explained that:
error: incr(k) is not a core constant expression because
lifetime of k began outside the expression incr(k)
This is exactly what is happening in the use_size() function with the foo argument, even if the size() function only use the N template parameter.
template <int N>
constexpr int size(const Foo<N>&) { return N; }
template <int N>
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
I was expecting Clang to be wrong in this case. It should evaluate your function call as being a constant expression, simply because you use only the template parameter, and not the object itself. Since you don't use the object in your constexpr function, there should be nothing prohibit compile time evaluation.
However, there's a rule in the standard that says object that began their lifetime preceding the constant expression such as a reference is not useable as constexpr.
There is a simple fix in that case. I think it didn't like the reference:
template <int N> // pass by value, clang is happy
void use_size(Foo<N> foo) { constexpr int n = size(foo); }
Here's a live example
Alternatively, you can also copy your foo object and use that local object:
template <int N>
void use_size(const Foo<N>& foo) {
auto f = foo;
constexpr int n = size(f);
}
Live example

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?
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).