static constexpr initialization chain within a class - c++

I have a regular class, let's call it Handler, which performs some algorithm invoked on demand in runtime.
The algorithm reads an array (m_arr), its content is known at compile-time so I want to utilize constexpr to initialize it.
I don't want an aggregate initializer (it might look ugly), I'd like to use a function which initializes the array.
For the sake of elegance and encapsulation I want to keep them as Handler's static members. The m_arr I'd like to qualify constexpr itself, because I want to init another array with another function basing on it (if I succeed with this one in the first place).
Currently I'm struggling with four propagating errors.
This is a draft of what I'm trying to achieve (with errors marked):
#include <array>
class Handler
{
static const int SIZE = 20;
static constexpr std::array<int, SIZE> initArr();
static constexpr std::array<int, SIZE> m_arr; //C2737 'private: static std::array<int, SIZE> const Handler::m_arr': 'constexpr' object must be initialized
//much other non-const stuff which this class handles...
};
constexpr std::array<int, Handler::SIZE> Handler::m_arr = Handler::initArr(); //C2131 expression did not evaluate to a constant
constexpr std::array<int, Handler::SIZE> Handler::initArr()
{
std::array<int, SIZE> arr; //C3250 'arr': declaration is not allowed in 'constexpr' function body
arr[0] = int(2); //C3249 illegal statement or sub-expression for 'constexpr' function
arr[1] = int(7); //C3249 illegal statement or sub-expression for 'constexpr' function
arr[2] = int(4); // -- || --
//...
return arr;
}
Apparently I'm doing something wrong here - or - I expect from the language something which it cannot provide (compiler - MSVC 2015/14.0).
Explanation of the reason of the errors (along with a closest working alternative) very very appreciated...

Generally speaking, static functions in classes cannot be used to initialize constexpr static data members because the function definitions are not considered at the point of initialization. You need to make it a free function and initialize the data member in the class body:
constexpr std::array<int, SIZE> init()
{
// ...
}
struct C {
static constexpr std::array<int, SIZE> arr = init();
};

Related

How to obtain constexpr `.size()` of a non-static std::array member

Given that std::array<T,N>::size is constexpr, in the snippet below
Why does it matter that Foo1::u is not a static member? The type is known at compile time and so is its size().
What's wrong with Foo2::bigger()?
Listing:
// x86-64 gcc 10.1
// -O3 --std=c++20 -pedantic -Wall -Werror
#include <array>
#include <cstdint>
union MyUnion {
std::array<uint8_t,32> bytes;
std::array<uint32_t,8> words;
};
struct Foo1 {
MyUnion u;
static constexpr size_t length {u.bytes.size()};
//invalid use of non-static data member 'Foo1::u'
};
struct Foo2 {
MyUnion u;
size_t x;
consteval int8_t length() const { return u.bytes.size(); };
bool bigger() const { return x > length(); }
//'this' is not a constant expression
};
I would like to keep std::array length in MyUnion declaration and not resort to
constexpr size_t LEN {32};
union MyUnion {
std::array<uint8_t,LEN> bytes;
std::array<uint32_t,LEN/4> words;
};
These situations are a bit different.
In the first one:
static constexpr size_t length {u.bytes.size()};
//invalid use of non-static data member 'Foo1::u'
You're trying to access a non-static data member without an object. That's just not a thing you can do. There needs to be some Foo1 associated with this expression. In the context of a non-static member function, it'd implicitly be this->u, but there still needs to be some object there. There's only one exception: you're allowed to write sizeof(Foo1::u).
In the second one:
consteval int8_t length() const { return u.bytes.size(); };
Here, the this pointer is not itself a constant expression (we don't know what it points to), so accessing anything through it fails. This is part of a broader case of constant expressions using unknown references in a way that doesn't really affect the constant-ness of the expression (see my blog post on the constexpr array size problem). I recently wrote a paper on this topic which was focused primarily on references, but this is a narrow extension on top of that.
Either way, for now this can't work either, because everything has to be a constant. So you'll have to resort to something along the lines of what you suggest: expose the constant you want separately.
I recommend "resorting to" a variable to define the size in the first place:
union MyUnion {
static constexpr std::size_t size = 32;
using byte = std::uint8_t;
using word = std::uint32_t;
std::array<byte, size> bytes;
std::array<word, size / sizeof(word)> words;
};
struct Foo1 {
using Union = MyUnion;
Union u;
static constexpr std::size_t length = Union::size;
};
Why does it matter that Foo1::u is not a static member?
Non-static members can be accessed only within member functions.
I recommend using std::variant instead of union. It's much easier to use.
You can directly get it from type
// direct
consteval static auto length() { return std::tuple_size<decltype(MyUnion::bytes)>::value; }
// with indirection
consteval static auto length() { return std::tuple_size<std::decay_t<decltype(u.bytes)>>::value; }
Or you can do it by create new instance.
// direct
consteval static auto length() { return MyUnion{.bytes={}}.bytes.size(); }
// just the member + indirection
consteval static auto length() { return decltype(u.bytes){}.size(); }
For the errors you got in your code
invalid use of non-static data member 'Foo1::u'
means you cannot use non-static data member u without instance (e.g. inside static member function).
'this' is not a constant expression*
means you cannot call this.length() (simply because it's consteval but this is not here)
Why? because it's what the standard say, otherwise there is no reason to use constexpr specifier at all because compiler can infer it, isn't it?
Related question:
Why is constexpr not automatic?
Why isn't std::array::size static?

Force a constexpr function to be compiled at compile time, even if calculation inside contains a non-const array, by making the returned obj constant?

I read about constexpr functions and when they do a compile time calculation. Now i am at a point, where i have to fill an array with new values, thus the array cannot be const.
From what i know, a constexpr function is definitely evaluated at compile time, when every value inside the function is const and used functions are constexpr which also only use const values. Furthermore, an other post stated, that it's possible to force a compile time compilation by making the returned obj const.
Lets say, my function is a constexpr, but has a non-const array inside, but the returned object will be const. Will my function calculation be evaluated at compile time(forced?).
template<int Size, typename T>
struct Array{
T array[Size];
Array(const T * a){
for(int i = 0; i < Size; i++){
array[i] = a[i];
}
}
};
template<typename T, int size>
class Example{
private:
Array<size, T> _array;
public:
constexpr explicit Example(T * arr):_array(arr){};
constexpr explicit Example(const T * arr):_array(arr){};
};
template<typename D, int size, typename ...buf, typename T>
constexpr auto calculations(const T & myObj){
D test1[2];
// calculation fills arr
return Example<D, size>(test1);
}
int main(){
const int size = 2;
const double test1[size] = {1,2};
const auto obj1 = Example<double, size>(test1); //compile time
//obj2 calculations during compile time or run-time?
const auto obj2 = calculations<double, size>(obj1);
}
From what i know, a constexpr function is definitely evaluated at compile time, when every value inside the function is const and used functions are constexpr which also only use const values. Furthermore, an other post stated, that it's possible to force a compile time compilation by making the returned obj const.
All of these statements are wrong. See below.
No, a call to a constexpr function is only guaranteed to be evaluated at compile-time if it is called in a context requiring a (compile-time) constant expression. The initializer of obj2 is not such a context, even if it is const.
You can force the initializer to be compile-time evaluated by declaring obj2 as constexpr. (Which however has very different meaning than const!)
Even then it is not going to work, because calculations<double, size>(obj1) is not actually a constant expression. obj1 is not a compile-time constant without declaring it constexpr as well. Similarly this doesn't work because test1 is not a constant expression without declaring it constexpr as well.
Then you also need to make the constructor of Array constexpr and you need to actually fill the values of test1 inside calculations, because accessing uninitialized values causes undefined behavior and undefined behavior makes expressions not constant expressions.
So all in all:
template<int Size, typename T>
struct Array{
T array[Size];
constexpr Array(const T * a) {
for(int i = 0; i < Size; i++){
array[i] = a[i];
}
}
};
template<typename T, int size>
class Example{
private:
Array<size, T> _array;
public:
constexpr explicit Example(T * arr):_array(arr){};
constexpr explicit Example(const T * arr):_array(arr){};
};
template<typename D, int size, typename ...buf, typename T>
constexpr auto calculations(const T & myObj){
D test1[2];
test1[0] = 0;
test1[1] = 1;
// calculation fills arr
return Example<D, size>(test1);
}
int main(){
const int size = 2;
constexpr double test1[size] = {1,2};
constexpr auto obj1 = Example<double, size>(test1); //compile time
//obj2 calculations during compile time or run-time?
constexpr auto obj2 = calculations<double, size>(obj1);
}
In C++20 there will be an alternative keyword consteval which one can use instead of constexpr on a function to force it to always be evaluated at compile-time. Currently there is no way to do that without making e.g. the destination of the return value a constexpr variable.
In fact your original code has undefined behavior. Because Array does not have a constexpr constructor, objects of that type can never be constructed in constant expressions. And because Example uses that type, it cannot be used in constant expressions either. This makes it illegal to put constexpr on its constructor, because a function declared constexpr causes undefined behavior if there isn't at least one valid set of template arguments and function arguments that would produce a constant expression. (The same then applies to calculations as well, because it uses Example.
So you must put constexpr on the constructor of Array in any case if your program is supposed to be well-formed.
Whether variables created inside the constant expression (e.g. inside calculations) are const does not matter at all.
from Microsoft:
A constexpr function is one whose return value can be computed at
compile time when consuming code requires it. Consuming code requires
the return value at compile time, for example, to initialize a
constexpr variable or provide a non-type template argument. When its
arguments are constexpr values, a constexpr function produces a
compile-time constant. When called with non-constexpr arguments, or
when its value isn't required at compile time, it produces a value at
run time like a regular function. (This dual behavior saves you from
having to write constexpr and non-constexpr versions of the same
function.)
so your calculate function will evaluated compile time if all parameters are constexpr

Is it possible to to create a vector filled with zeros of size N using template meta programming in c++11

I'm learning more about template meta programming in c++ and after doing the standard generate primes and factorial in compile time I'm now trying to create a Matrix class in compile time. A subproblem of this is to create a vector of size N filled with zeros. Is this possible to do with template meta programming?
I'm thinking something like this.
template<int N>
struct Vec {
static std::vector<int> vec;
constexpr Vec(int count) {
for (int i = 0; i < count; ++i) {
vec.push_back(0);
}
}
};
But the compiler says that ´for-loops´ are not allowed in an constexpr constructor. Is there any way to achieve functionality like this in compile time.
A subproblem of this is to create a vector of size N filled with zeros. Is this possible to do with template meta programming?
If with "vector" do you mean exactly "std::vector", no: isn't possible in C++11, C++14 or C++17. Maybe with future versions of the standard; not now.
If for "vector" do you accept a "std::array", yes: it's possible and trivially simple (no initialization function required)
constexpr std::array<int, 10> a{}; // zero initialized !
I suppose that your Vec could be written
template <std::size_t N> // better std::size_t for a size (IMHO)
struct Vec
{
std::array<int, N> vec; // no static for a member, please
constexpr Vec () : vec{}
{ }
};
but seems to me that you can use directly a std::array.
But the compiler says that ´for-loops´ are not allowed in an constexpr constructor. Is there any way to achieve functionality like this in compile time.
The compiler complain about a problem of different type: it's saying that, in C++11, a constexpr function can't contain a for-loop.
In C++11 a constexpr function can contain (simplifying a lot) only a return instruction. A constexpr constructor must be empty; only initialization list.
If you want a more complex constexpr function (with a for-loop in it) you need C++14.
I wrote over main:
constexpr size_t N = 100;
constexpr std::vector<int> ZEROES(N, 0);
Which led to the conclusion that:
7:39: error: the type 'const std::vector<int>' of constexpr variable 'ZEROES' is not literal
In file included from /usr/include/c++/4.9/vector:64:0,
from 4:
/usr/include/c++/4.9/bits/stl_vector.h:214:11: note: 'std::vector<int>' is not literal because:
class vector : protected _Vector_base<_Tp, _Alloc>
^
/usr/include/c++/4.9/bits/stl_vector.h:214:11: note: 'std::vector<int>' has a non-trivial destructor
Which means that you will not be able to create a compile time std::vector of any kind. However you can make an array, I don't know if it helps:
constexpr int ZEROES2[N] = {0};

Mutating an int inside a constexpr function

Why can I do this:
constexpr auto i_can() {
int a = 8;
a = 9;
//...
}
But I cannot do this:
constexpr auto i_cannot() {
std::array<int, 10> arr{};
//I cannot
arr[5] = 9;
}
My questions are:
If I can mutate an int, why can I not mutate an int that is inside the array?
Is this a language limitation (C++14) or a standard library spec problem? reference std::array<T, N>::operator[](size_t) is not currently constexpr.
Modification of objects inside constexpr functions has been introduced with C++14. However, while modifying e.g. a scalar by an assignment is fine, modifying a class object through a member function still needs that member function to be constexpr. And unfortunately, as you mentioned, the current std::array specification does not declare the non-const operator[] as constexpr.
Hence, §7.1.5/5 makes your definition ill-formed:
For a non-template, non-defaulted constexpr function […], if no
argument values exist such that an invocation of the function […]
could be an evaluated subexpression of a core constant expression
(5.20), […], the program is ill-formed; no diagnostic required.
You can temporarily use a more modern implementation if you want full constexpr-ness. E.g. Constainer::Array.
It is a limitation of the standard library since you can modify a plain C array in a constexpr:
#include <iostream>
constexpr auto demo()
{
int arr[10] = {};
arr[5] = 9;
return arr[5];
}
int main()
{
static_assert(demo() == 9, "");
std::cout << demo() << std::endl;
return 0;
}
DEMO
output
9
If you added constexpr to operator[] of an implementation of array, you could also use this operator inside a constexpr.
DEMO

Why constexpr must be static?

An attempt to create a member of a struct with constexpr attribute without being static result in a compiler error(see below). Why is that? for a single constant value will I have this value in memory until program is terminatted instead of just scope of struct? should I back to use a macro?
struct foo
{
constexpr int n = 10;
// ...
};
error: non-static data member cannot be constexpr; did you intend to make it static?
I don't know the official rational. But surely it could lead to confusion. I, for one, can't see what it means for a non-static data member to be constexpr. Are you able to do the following?
struct foo {
constexpr int n = 10;
constexpr foo() { }
constexpr foo(int n):n(n) { } // overwrite value of n
};
Or does it mean that the initializer must be constant always, i.e you are not allowed to write the above (because n is not constant/could potentially non-constant) but allowed to say
foo f = { 10 };
The rule that constexpr int n is simply ill-formed rather than being implicitly static seems good to me, as its semantics would not be clear IMO.