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.
Related
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?
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();
};
I'm used to definition my constants with enum { my_const = 123; }, since in classes, using static constexpr requires some code outside of the class definition (see this question). But - what about in function bodies? Lately I've been noticing people just having constexpr variables in their functions (not even bothering to const them actually), and I was wondering whether I'm a fool who's behind the times with my
int foo(int x)
{
enum : int { bar = 456 };
return x + bar;
}
So, my question is: Is there any benefit to using enum's within function bodies rather than constexpr variables?
You can accidentally or on purpose force ODR-existence of bar if it was a constexpr int bar = 456;, this is not possible with enum : int { bar = 456 };.
This may or may not be an advantage on either side.
For example
int baz(int const* ptr ) {
if (ptr) return 7; return -1;
}
int foo(int x)
{
// enum : int { bar = 456 };
constexpr int bar = 456;
return x + baz(&bar);
}
the enum version doesn't compile, the constexpr int one does. A constexpr int can be an lvalue, an enumerator (one of the listed enum constants) cannot.
The enum values aren't actually an int, while the constexpr int is actually an int. This may matter if you pass it to
template<class T>
void test(T) {
static_assert(std::is_same<T,int>::value);
}
one will pass the test; the other will not.
Again, this could be an advantage, a disadvantage, or a meaningless quirk depending on how you are using the token.
A one-liner based on #Yakk's (but this is my own take):
using enum-based constants may be necessary if you cannot allow your constant to exist as a "variable" at run time . With an enum, regardless of what you do - it will have no address and no memory space taken up (and not only because of compiler optimizations which may or may not occur).
In other cases there doesn't seem to be a compelling reason to prefer one over the other.
In C++11 a new feature was introduced where the programmer can initialize class member variables inside class's definition, see code below:
struct foo
{
int size = 3;
int id = 1;
int type = 2;
unsigned char data[3] = {'1', '2', '3'};
};
Is this initialization takes place during compile time or this feature is just syntactic sugar and member variables are initialized in the default constructor?
First of all yes, as stated before, it is syntactic sugar. But since the rules can be too much to remember, here's a logical experiment to help you figure out what happens in compile time and what not
You have your c++11 class that features in class initializers
struct foo { int size = 3; };
And another class that will help us with our experiment
template<int N>
struct experiment { enum { val = N }; };
Let our hypothesis H0 be that initialization does happen in compile time, then we could write
foo a;
experiment<a.size> b;
No luck, we fail to compile. One could argue that failure is due to foo::size being non constant so lets try with
struct foo { const int size = 3; }; // constexpr instead of const would fail as well
Again, as gcc informs us
the value of ‘a’ is not usable in a constant expression
experiment b;
or (more clearly) visual studio 2013 tells us
error C2975: 'N' : invalid template argument for 'example', expected compile-time constant expression
So, we have to discard H0 and deduce that initialization does not happen in compile time.
What would it take to happen in compile time
There is an old syntax that does the trick
struct foo { static const int size = 3; };
Now this compiles but beware this is (technically and logically) no longer in class initialization.
I had to lie for a little to make a point, but to expose the whole truth now : Message errors imply that a is the real problem. You see, since you have an instance for an object (Daniel Frey also mentions this) memory (for members) has to be initialized (at runtime). If the member was (const) static, as in the final example, then it's not part of the subobjects of a(ny) class and you can have your initialization at compile time.
In-class initialisers for member-variables are syntactic sugar for writing them in the constructor initialiser list, unless there's an explicit initialiser already there, in which case they are ignored.
In-class initialisers of static const members are for constant literals, a definition is still needed (though without initialiser).
C++ has the "as if"-rule from C, so anything resulting in the prescribed observed behavior is allowed.
Specifically, that means static objects may be initialised at compile-time.
It's just syntactic sugar. Also consider that an instance usually means memory which has to be initialized with the correct values. Just because these values are provided with a different syntax does not change the fact that the memory needs to be initialized - which happens at run-time.
Its essentially syntactic sugar for a user provided constructor which initializes the values. You are providing default values for data members. When you ask whether this happens at compile time or run time, the answer depends on the context its used in.
Hopefully, these examples will help. Try them in http://gcc.godbolt.org and see the dissassembly and compilation errors.
struct S { int size = 3; };
//s's data members are compile time constants
constexpr S s = {};
//r's data members are run time constants
const S r = {};
//rr's data members are run time constants,
//but we don't know the values in this translation unit
extern const S rr;
template <int X> class Foo {};
//Ok, s.size is a compile time expression
Foo<s.size> f;
//Error, r.size is not a compile time expression
Foo<r.size> g;
//Compile time expression, this is same as return 3;
int foo() { return s.size; }
//This also works
constexpr int cfoo() { return s.size; }
//Compiler will optimize this to return 3; because r.size is const.
int bar() { return r.size; }
//Compiler cannot optimize, because we don't know the value of rr.size
//This will have to read the value of rr.size from memory.
int baz() { return rr.size; }
As others have shown, static data members (and global variables, same thing essentially) for primitive types such as ints and floats have some weird rules where they can be const but still be used in compile time contexts as if they were constexpr. This is for backwards compatibility with C and the lack of the constexpr feature in the past. Its unfortunate now because it just makes understanding constexpr and what differentiates run time expressions from compile time expressions more confusing.
Having int size = 3; is exactly equivalent to having int size; and then each constructor that doesn't already have size in its initializer list (including compiler-generated constructors) having size(3) there.
Strictly speaking C++ doesn't have a distinction between "compile-time" and "run-time".
Requirements
I want a constexpr value (i.e. a compile-time constant) computed from a constexpr function. And I want both of these scoped to the namespace of a class, i.e. a static method and a static member of the class.
First attempt
I first wrote this the (to me) obvious way:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x says to that:
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x complains:
error: field initializer is not constant
Second attempt
OK, I thought, perhaps I have to move things out of the class body. So I tried the following:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3 will compile that without complaints. Unfortunately, my other code uses some range-based for loops, so I have to have at least 4.6. Now that I look closer at the support list, it appears that constexpr would require 4.6 as well. And with g++-4.6.3 I get
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
This sounds really strange to me. How do things “differ in constexpr” here? I don't feel like adding -fpermissive as I prefer my other code to be rigurously checked. Moving the foo implementation outside the class body had no visible effect.
Expected answers
Can someone explain what is going on here? How can I achieve what I'm attempting to do? I'm mainly interested in answers of the following kinds:
A way to make this work in gcc-4.6
An observation that later gcc versions can deal with one of the versions correctly
A pointer to the spec according to which at least one of my constructs should work, so that I can bug the gcc developers about actually getting it to work
Information that what I want is impossible according to the specs, preferrably with some insigt as to the rationale behind this restriction
Other useful answers are welcome as well, but perhaps won't be accepted as easily.
The Standard requires (section 9.4.2):
A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer.
Your first code is correct. It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (e.g. ideone.com is still stuck on gcc 4.5).
This isn't possible, because unfortunately the Standard precludes initializing a static constexpr data member in any context where the class is complete. The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static.
The most likely reason for this is that constexpr variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:
an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;
Consider:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1) Ilya's example should be invalid code based on the fact that the static constexpr data member bar is initialized out-of-line violating the following statement in the standard:
9.4.2 [class.static.data] p3: ... A static data member of literal type can be declared in the class definition with the constexpr specifier;
if so, its declaration shall specify a brace-or-equal-initializer in
which every initializer-clause that is an assignment-expression is a
constant expression.
2) The code in MvG's question:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
is valid as far as I see and intuitively one would expect it to work because the static member foo(int) is defined by the time processing of bar starts (assuming top-down processing).
Some facts:
I do agree though that class C1 is not complete at the point of invocation of foo (based on 9.2p2) but completeness or incompleteness of the class C1 says nothing about whether foo is defined as far as the standard is concerned.
I did search the standard for the definedness of member functions but didn't find anything.
So the statement mentioned by Ben doesn't apply here if my logic is valid:
an invocation of an undefined constexpr function or an undefined
constexpr constructor outside the definition of a constexpr function
or a constexpr constructor;
3) The last example given by Ben, simplified:
class C1
{
constexpr static int foo() { return bar; }
constexpr static int bar = foo();
};
looks invalid but for different reasons and not simply because foo is called in the initializer of bar. The logic goes as follows:
foo() is called in the initializer of the static constexpr member bar, so it has to be a constant expression (by 9.4.2 p3).
since it's an invocation of a constexpr function, the Function invocation substitution (7.1.5 p5) kicks in.
Their are no parameters to the function, so what's left is "implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization." (7.1.5 p5)
the return expression is just bar, which is a lvalue and the lvalue-to-rvalue conversion is needed.
but by bullet 9 in (5.19 p2) which bar does not satisfy because it is not yet initialized:
an lvalue-to-rvalue conversion (4.1) unless it is applied to:
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.
hence the lvalue-to-rvalue conversion of bar does not yield a constant expression failing the requirement in (9.4.2 p3).
so by bullet 4 in (5.19 p2), the call to foo() is not a constant expression:
an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a constant expression
#include <iostream>
class C1
{
public:
constexpr static int foo(constexpr int x)
{
return x + 1;
}
static constexpr int bar;
};
constexpr int C1::bar = C1::foo(sizeof(int));
int main()
{
std::cout << C1::bar << std::endl;
return 0;
}
Such initialization works well but only on clang
Probably, the problem here is related to the order of declaration/definitions in a class. As you all know, you can use any member even before it is declared/defined in a class.
When you define de constexpr value in the class, the compiler does not have the constexpr function available to be used because it is inside the class.
Perhaps, Philip answer, related to this idea, is a good point to understand the question.
Note this code which compiles without problems:
constexpr int fooext(int x) { return x + 1; }
struct C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = fooext(5);
};
constexpr static int barext = C1::foo(5);