For the following code:
int main()
{
constexpr char ch[64] = "Hello";
auto res1 = ch;
constexpr auto res2 = ch;
}
The assignment of res1 is OK, but the assignment of res2 cannot compile. So this means we can get an address of a compile-time object at run time, but we cannot get the address at compile time. Can you please tell me why C++ block the compilation of that line?
Thanks a lot!
Marking a variable constexpr does not change its storage duration. ch is still a variable local to the function with automatic storage duration and lives only until the end of the function scope. Each time the function is called ch is a new object. ch does not gain static storage duration through the constexpr.
Therefore it is not reasonable to take a pointer to the variable as a constant expression. The value of the pointer would need to be different in every function invocation, violating the assumption that a constant expression evaluates to a single compile-time constant.
If you want the variable to have static storage duration, so that there is only one instance of it in the whole program, then add static in addition to constexpr. The address of static storage duration objects can be used in constant expressions and as result of constant expressions. There is no ambiguity in the pointer value between different instances of the variable.
Related
I was trying to compute an array in compilation time to speed up some functions while i encountered an error which I was unable to resolve with the help of cppreference.
The code boils down to this:
#include <cstddef>
#include <array>
template<typename T, size_t size>
constexpr auto giveArray()
{
std::array<T, size> arr;
for(size_t i = 0; i < size; ++i)
arr[i] = 0;
return arr;
}
constexpr auto arr = giveArray<int,10>().data();
While compiling with: "$ g++ -std=c++20 code.cpp" on ubuntu i get the error that .data() is not a constexpr function while it definitely is. Why am i getting this error and how it can be fixed while still running this function in compilation time and storing only a pointer, not the std::array object?
storing only a pointer, not the std::array object?
You can't.
The rules of C++ are not suspended just because your code executes at compile time (indeed, not suspending those rules is half the point of constexpr code). The array needs to exist in order for a pointer to it to be meaningful. That means the array object needs to continue to be around when someone uses that pointer.
Getting a pointer to a temporary that gets destroyed will leave that pointer pointing to a destroyed object. This is just as true in compile-time code as runtime code. And using that pointer is just as non-functional at compile-time as it would be at runtime. Indeed, it's less functional, as UB (which is what accessing that pointer will provoke) at compile-time must be diagnosed and turned into a compile error.
To initialize a constexpr variable you need an initializer that is a constant expression. But giveArray<int,10>().data(); is not a constant expression.
From here :
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:
if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value,
if the value is of pointer-to-member-function type, it does not designate an immediate function, and
if the value is an object of class or array type, each subobject satisfies these constraints for the value.
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
The expression giveArray<int,10>().data(); has the value category of prvalue , so it is not a glvalue expression. That means it has to be "a prvalue core constant expression whose value satisfies the following constraints" to be a constant expression. Since the expression evaluates to a pointer type, it has to meet this condition :
if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value
In this case, it is none of those things so it is not a constant expression. However, if you change the code to like :
const auto my_array = giveArray<int,10>();
constexpr auto arr = my_array.data();
my_array is a global variable, which implies it has static storage duration. data() returns a pointer to that storage, which makes it a constant expression.
In short, this rule makes it so you can't have a pointer with an invalid pointer value when executing code at compile time.
You can try with
constexpr auto a0 = giveArray<int,10>();
constexpr auto arr = a0.data();
I mean... whith
constexpr auto arr = giveArray<int,10>().data();
you try to initialize arr with a pointer to memory that is set free immediately after.
You can solve fixing the std::array received from giveArray()
constexpr auto a0 = giveArray<int,10>();
so arr receive a pointer to a memory that remain stable after the initialization
constexpr auto arr = a0.data();
After reading this and this I still feel confused about this kind of expressions:
static constexpr int = 0;
AFAIK, in C++:
static ensures life-time memory address along whole execution and safe initialization with concurrent threads
constexpr ensures time-compilation evaluation as rvalue, which means it shall have no memory address
They look contradictory to me. static ensures the variable will have a long-time memory address whereas constexpr ensures the opposite assumption. Surprisingly, the discussion in the first link mentions this:
constexpr int x = 3;
const int* p = &x;
How can we even obtain the memory address of x if it is an rvalue?
Could anyone explain it?
static has a number of meanings. In classes (per your comment), it means that the member is associated with the class, and not a specific instance (object) of that class.
For a constexpr, that makes a lot of sense. That's typically initialized by a value known to the compiler, and not from ctor arguments.
This question already has answers here:
Do pointers to string literals remain valid after a function returns?
(4 answers)
Closed 3 years ago.
I have a lambda which uses a const char * defined in function scope. The lambda is executed after the function returns.
void function() {
const char * UNLOCK_RESULT = "unlockResult";
..
..
TestStep::verifyDictionary(test, [UNLOCK_RESULT](TestStep::Dictionary& dict){
return 1 == dict[UNLOCK_RESULT].toInt();
});
...
}
If I look at the assembly that GCC generates, the actual char array holding the "unlockResult" is statically allocated. But I was wondering if this is guaranteed by the standard as in theory the const char * is of function scope. Is the capture of const char * undefined behaviour or is this allowed by the standard because of some exception around C++ handling of const char * strings.
I know I can change:
const char * UNLOCK_RESULT=""
to
constexpr const char * UNLOCK_RESULT=""
And then the discussion falls away because I don't even need to capture it, but I am curious about the const char * case.
You are mixing 2 concepts. In function scope is variable UNLOCK_RESULT itself, so if you try to use reference or pointer to the variable then you would get UB. But as you use a value of that variable which is pointer to static data you are totally fine as such pointer is not invalidated after function terminates.
If your question is if a pointer to string literal is valid after function terminates, then yes it is valid and guaranteed by C++ standard to have static duration:
Evaluating a string-literal results in a string literal object with
static storage duration, initialized from the given characters as
specified above. Whether all string literals are distinct (that is,
are stored in nonoverlapping objects) and whether successive
evaluations of a string-literal yield the same or a different object
is unspecified.
(emphasis is mine)
Btw it is an old habit to use identifiers for constants with all uppercase as they used to be defined in preprocessor. When you have compile time identifiers for constants uppercase you would hit exactly the same problem this habit tried to minimize.
The behavior is well-defined.
The lambda [UNLOCK_RESULT](TestStep::Dictionary& dict){...} captures the pointer by-value. So UNLOCK_RESULT inside the lambda is a copy of the original UNLOCK_RESULT, the latter does not have to exist for the duration of the lambda.
Now the string literal "unlockResult" has static storage duration, which means it's allocated at program start and remains available for the duration of the program:
All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program.
static const char* const test_script = "test_script";
When and why would you use a statement such as the one above?
Does it have any benefits?
Why use a char* instead of a constant character?
A "constant Character pointer" (const char*) is a constant already and cannot be changed; Then why use the word static in the front of it? What good does it do?
A const char *p is not a constant pointer. It's a modifiable pointer to a const char, that is, to a constant character. You can make the pointer point to something else, but you cannot change the character(s) to which it points. In other words, p = x is allowed, but *p = y is not.
A char * const is the opposite: a constant pointer to a mutable character. *p = y is allowed, p = x is not.
A const char * const is both: a constant pointer to a constant character.
Regarding static: this gives the declared variable internal linkage (cannot be accessed by name from outside the source file). Since you're asking about both C and C++, note that this is where they differ.
In C++, a variable declared const and not explicitly declared extern has internal linkage by default. Since the pointer in question is const (I'm talking about the second const), the static is superfluous in C++ and doesn't do anything.
That's not the case in C, where const variables cannot be used as constant expressions and do not have internal linkage by default. So in C, the static is necessary to give test_script internal linkage.
The above discussion of static assumes the declaration is at file scope (C) or namespace scope (C++). If it's inside a function, the meaning of static changes. Without static, it would a normal local variable in the function—each invocation would have its own copy. With static, it receives static storage duration and thus persists between function calls—all invocations share that one copy. Since you're asking about both C and C++, I am not going to discuss class scope.
Finally, you asked "why pointer instead of characters." This way, the pointer points to the actual string literal (probably somewhere in the read-only part of the process's memory). One reason to do that would be if you even need to pass test_script somewhere where a const char * const * (a pointer to a constant pointer to constant character(s)) is expected. Also, if the same string literal appears multiple times in source code, it can be shared.
An alternative would be to declare an array:
const char test_script[] = "test_script";
This would copy the string literal into test_script, guaranteeing that it has its own copy of the data. Then you can learn the length at compile-time from sizeof test_script (which includes the terminating NUL). It will also consume slightly less memory (no need for the pointer) if it's the only occurence of that string literal. However, as it will have its own copy of the data, it cannot share the storage of the string literal (if that is used elsewhere in the code as well).
If I never use the address of a static const variable, is memory allocated for it when using a reasonably modern compiler?
It depends on the type of the variable, and on whether "constant" also means "constant expression". Example:
static const Foo = get_foo(std::cin);
static const int q = argc * 3;
static const std::string s(gets());
These variables are const, but blatantly need an actual allocation.
On the other hand, the following constant expression may never have physical storage:
static const int N = 1000;
static const std::shared_ptr<void> vp(); // constexpr constructor!
Most importantly, static constexpr member variables don't need a definition if you're careful:
struct Bar
{
int size() const { return N; }
static const int N = 8;
};
// does NOT need "const int Bar::N;"
There is chance that it isn't, but that doesn't matter. You can't rely on implementation details, only on the standard.
In practice, space for static storage can be allocated as part of the initial binary loading, or by the runtime during startup; but will always happen before user code is encountered.
In addition to the constraints that Kerrek SB mentions, the storage for a const expr value could be eliminated if the value itself is never used at runtime.
This wouldn't necessarily mean that the value needs to not be evaluated - if a static const expr were only used as a branch condition, that condition may be evaluated statically and other code paths may not be generated or may be excluded by the optimiser.
Pretty much any storage with static duration may be eliminated if the implementation can guarantee behaviour as though the storage were present - i.e. a comparison expression that can be evaluated at compile time - like a different const expr, a pointer comparison where the rhs is known to be an alias to a different variable, or perhaps an incompatible type. It may also be eliminated if the value is only read into variables that are never read themselves; or where the value may be reduced to a const expr.
struct Foo{};
static Foo bar; // static instance
Foo* func() {
if ( ! (&bar) ) { // always non-NULL
// this block may be eliminated
Foo* myCopy(new Foo(bar));
return myCopy;
}
// so 'bar' is never referred to, and we know it has no side-
// effects, so the static variable can be eliminated
return new Foo();
}
3.7.1 Static storage duration
2. If an object of static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy may be eliminated as specified in 12.8.
Memory for global variables is reserved by the linker, not the compiler. So the question is whether the linker is smart enough to not reserve space for global variables that are only used by value.
It depends on the type and use of such data; for example, floating point constants generally must be loaded from memory, so they must have storage even if you don't directly use the address.
Having said that, the standard does specify whether you can optimize out static storage (3.7.1.2: [basic.stc.static]):
If a variable with static storage duration has initialization or a
destructor with side effects, it shall not be eliminated even if it
appears to be unused, except that a class object or its copy/move may
be eliminated as specified in 12.8.
So if the static const variable has a constructor or destructor, it cannot be optimized out (although some compilers/linkers will do this anyway). If it doesn't, it can. Whether it will depends on the linker.