Mutating an int inside a constexpr function - c++

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

Related

Why can't constexpr be used for non-const variables when the function only uses the types?

Maybe the title is not clear, so concretely:
#include <type_traits>
template<typename T>
constexpr int test(T)
{
return std::is_integral<T>::value;
}
int main()
{
constexpr int a = test(1); // right
constexpr int b = test(1.0); // right
int c = 2;
constexpr int d = test(c); // ERROR!
return 0;
}
In fact, the function doesn't use anything but the type of the parameter, which can be determined obviously in the compilation time. So why is that forbidden and is there any way to make constexpr get the value when only the type of parameter is used?
In fact, I hope to let users call the function through parameters directly rather than code like test<decltype(b)>, which is a feasible but not-convenient-to-use way, to check if the types of parameters obey some rules.
Just take T by reference so it doesn't need to read the value:
template<typename T>
constexpr int test(T&&)
{
return std::is_integral<std::remove_cvref_t<T>>::value;
}
You can even declare test with consteval, if you want to.
(Note that stripping cv-qualifiers isn't necessary in this instance; cv-qualified integral types satisfy the std::is_integral trait.)
Why can't constexpr be used for non-const variables when the function only uses the types?
Because the call expression test(c) is not a constant expression and hence it cannot be used as an initializer for d.
Basically, for the call expression test(c) to be a constant expression, c must be a constant expression. It doesn't matter whether c is used or not inside the function itself. This is why, the call expressions test(1) and test(1.0) works and can be used as an initializer for a and b respectively.

Legitimate to initialize an array in a constexpr constructor?

Is the following code legitimate?
template <int N>
class foo {
public:
constexpr foo()
{
for (int i = 0; i < N; ++i) {
v_[i] = i;
}
}
private:
int v_[N];
};
constexpr foo<5> bar;
Clang accepts it, but GCC and MSVC reject it.
GCC's error is:
main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
15 | constexpr foo<5> bar;
| ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
4 | constexpr foo()
| ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
12 | int v_[N];
| ^~
If this kind of code were OK, I could cut quite a few uses of index_sequences.
Trivial default initialisation was prohibited in a constexpr context until C++20.
The reason, I'm guessing, is that it is easy to "accidentally" read from default-initialised primitives, an act which gives your program undefined behaviour, and expressions with undefined behaviour are straight-up prohibited from being constexpr (ref). The language has been extended though so that now a compiler must check whether such a read takes place and, if it doesn't, the default-initialisation should be accepted. It's a bit more work for the compiler, but (as you've seen!) has substantial benefits for the programmer.
This paper proposes permitting default initialization for trivially default constructible types in constexpr contexts while continuing to disallow the invocation of undefined behavior. In short, so long as uninitialized values are not read from, such states should be permitted in constexpr in both heap and stack allocated scenarios.
Since C++20, it's legal to leave v_ "uninitialised" like you have. Then you've gone on to assign all its elements values, which is great.
While this does not directly answer your question, I thought it was at least worth mentioning. You could simply use in-class initialization and zero-initialize the array:
int v_[N]{};
Another way, without initializing the array first, is to (privately) inherit from std::array. Oddly enough, this actually gets accepted by GCC but not by Clang:
#include <array>
template<int N>
struct foo : private std::array<int, N> {
constexpr foo() {
for (auto i = int{}; i < N; ++i) {
(*this)[i] = i;
}
}
};
constexpr foo<5> bar;

static constexpr initialization chain within a class

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

Can't compile with static constexpr under pre-C++17 mode

Why doesn't the following minimal example compile with c++11 nor c++14, but compiles in c++17 and c++2a?
#include <iostream>
#include <limits>
#include <vector>
// works:
// static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
class Classy {
// does not work in c++11 (constexpr introduced) nor c++14:
// works if c++17 or newer:
static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
int VALUE_LIMIT_B = std::numeric_limits<int>::max();
public:
explicit Classy();
std::vector<int> classy;
};
Classy::Classy() {
// does not work:
classy.resize(3, VALUE_LIMIT_A);
// works:
// classy.resize(3, std::numeric_limits<int>::max());
// works:
// std::cout << VALUE_LIMIT_A;
// works:
// classy.resize(3, VALUE_LIMIT_B);
}
// required in c++11 and c++14
// constexpr int Classy::VALUE_LIMIT_A;
int main() {
Classy classy{};
for (const auto& elem : classy.classy) {
std::cout << elem << ",";
}
std::cout << "\n";
}
Here is output with c++11:
$ g++ -std=c++11 main.cpp && ./a.out
/tmp/ccon7pPo.o: In function `Classy::Classy()':
main.cpp:(.text+0x31): undefined reference to `Classy::VALUE_LIMIT_A'
collect2: error: ld returned 1 exit status
Here is output with c++17:
$ g++ -std=c++17 main.cpp && ./a.out
2147483647,2147483647,2147483647,
Because since C++17, the definition of the constexpr static data member at namespace scope is not required again.
If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11) is odr-used, a definition
at namespace scope is still required, but it cannot have an
initializer. This definition is deprecated for constexpr data members (since C++17).
struct X {
static const int n = 1;
static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used
const int X::n; // … so a definition is necessary
constexpr int X::m; // … (except for X::m in C++17)
If a static data member is declared constexpr, it is implicitly inline
and does not need to be redeclared at namespace scope. This
redeclaration without an initializer (formerly required as shown
above) is still permitted, but is deprecated. (since C++17)
And note that std::vector::resize takes the 2nd parameter by reference; which cause VALUE_LIMIT_A to be odr-used for classy.resize(3, VALUE_LIMIT_A);.
Since C++17, with the introduction of inline variables, static constexpr data members are implicitly inline variables:
[dcl.constexpr]
1 ... A function or static data member declared with the constexpr
specifier is implicitly an inline function or variable
([dcl.inline])...
Inline variables, like inline functions, are defined in every translation units they are used in. And the compiler resolves the multiple definitions into a single one. This means, the unlike C++14, there is no need to explicitly provide an out of class definition for a static constexpr variable for the sake of the ODR, the compiler takes care of it.
You can also get away with it in C++14, too. Like the other answer mentions, it is resize that ODR-uses the static data member. You can work around it, though:
classy.resize(3, int(VALUE_LIMIT_A));
While it looks superfluous, it in fact has different behavior to using the constant directly. This creates an integer temporary, with the value of the constant. But it doesn't ODR-use the constant. The temporary is bound to the reference instead, and so the problem is avoided. While it's better to define the constant in pre-C++17 code, you can use this trick to adapt code you have no control over.

constexpr member functions that don't use this?

Please consider the following two C++14 programs:
Program 1:
struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
Program 2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
Are neither, either or both of these programs ill-formed?
Why/why not?
Both programs are well-formed. The C++14 standard requires that s.f() be a constant expression because it is being used to initialize a constexpr variable, and in fact it is a core constant expression because there's no reason for it not to be. The reasons that an expression might not be a core constant expression are listed in section 5.19 p2. In particular, it states that the evaluation of the expression would have to do one of several things, none of which are done in your examples.
This may be surprising since, in some contexts, passing a non-constant expression to a constexpr function can cause the result to be a non-constant expression even if the argument isn't used. For example:
constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr auto y = f(x); // ill-formed
}
However, the reason this is ill-formed is because of the lvalue-to-rvalue conversion of a non-constant expression, which is one of the things that the evaluation of the expression is not allowed to do. An lvalue-to-rvalue conversion doesn't occur in the case of calling s.f().
I can't seem to find a compelling passage or example in the standard that directly addresses the issue of calling a constexpr member function on a non-constexpr instance, but here are some that may be of help (from draft N4140):
[C++14: 7.1.5/5]:
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.
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
From this I take that the program is not outright ill-formed just because a constexpr function has a possible non-constexpr path.
[C++14: 5.19]:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
This is somewhat closer to your example programs, here a constexpr constructor may reference a non-constexpr variable depending on the value of the argument, but there is no error if this path is not actually taken.
So I don't think either program you presented should be ill-formed, but I cannot offer convincing proof :)
This sounds like a quiz question, and not presented by a student, but the professor testing the public on stackoverflow, but let's see...
Let's start with the One Definition Rule. It's clear neither version violates that, so they both pass that part.
Then, to syntax. Neither have syntax failures, they'll both compile without issue if you don't mind the potential blend of a syntax and semantic issue.
First, the simpler semantic issue. This isn't a syntax problem, but f(), in both versions, is the member of a struct, and the function clearly makes no change to the owning struct, it's returning a constant. Although the function is declared constexpr, it is not declared as const, which means if there were some reason to call this as a runtime function, it would generate an error if that attempt were made on a const S. That affects both versions.
Now, the potentially ambiguous return g(S()); Clearly the outer g is a function call, but S may not be so clear as it would be if written return g(S{}); With {} initializing S, there would be no ambiguity in the future should struct S be expanded with an operator() (the struct nearly resembles a functor already). The constructor invoked is automatically generated now, and there is no operator() to create confusion for the compiler at this version, but modern C++14 is supposed to offer clearer alternatives to avoid the "Most Vexing Parse", which g(S()) resembles.
So, I'd have to say that based on semantic rules, they both fail (not so badly though).