The following program compiles:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
int main()
{
Test<&var> test;
}
This one, however, doesn't, which is a surprise for me:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
Test<ptr> test; //FAIL! Expected constant expression.
}
Alternative example:
int main()
{
const int size = 42;
int ok[*&size]; //OK
const int * const pSize = &size;
int fail[*pSize]; //FAIL
}
I have concluded that a pointer just can't be a constant expression regardless of whether it's const and initialized with a constant expression.
Questions:
Is my conclusion true?
If so, why can't a pointer be a constant expression? If not, why don't the above programs compile?
Does C++0x(C++11, if you will) change anything?
Thanks for any insights!
It's a bit more complicated. In C++03 and C++11, &var is a constant expression if var is a local static / class static or namespace scope variable. This is called an address constant expression. Initializing a class static or namespace scope pointer variable with that constant expression is guaranteed to be done before any code is run (static initialization phase), because of it being a constant expression.
However only since C++11, a constexpr pointer variable that stores the address &var can also be used as an address constant expression and only since C++11, you can dereference an address constant expression (actually, you can dereference even more - even local array element addresses, but let's keep it ontopic) and if it refers to a constant integral variable initialized prior to the dereference or a constexpr variable, you again get a constant expression (depending on the type and value category, the kind of constant expression may vary). As such, the following is valid C++11:
int const x = 42;
constexpr int const *px = &x;
// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }
If so, why can't a pointer be a constant expression? If not, why don't the above programs compile?
This is a known limitation in the Standard's wording - it currently only allows other template parameters as arguments or & object, for a template parameter of pointer type. Even though the compiler should be capable of doing much more.
It's still not allowed in C++0x. temp.arg.nontype requires:
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and
external or internal linkage or a function with external or internal linkage, including function templates
and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as
& id-expression, except that the & may be omitted if the name refers to a function or array and shall
be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
original answer:
In C++03, only integral expressions can be constant expressions.
Because the standard says so (naturally).
In C++0x, n3290 includes examples using constexpr on a pointer. So what you are trying should now be possible, although you must now use the constexpr keyword instead of top-level const.
There's also a gcc bug involved, g++ rejects the standard draft's own examples of valid constexpr usage.
The problem is because your C++ program can be loaded at any point in memory, and so the address of a global var may be different each time you run the program. What happens if you run your program twice? var is obviously in two different locations then.
Even worse, in your example, you take the address of a variable on the stack! look at this:
void myfunction( unsigned int depth) {
const int myvar = depth;
const int * const myptr = &myvar;
if (depth)
myfunction(depth-1);
}
If main calls myfunction(3), then 3 myvars are created at seperate locations. There's no way for the compile time to even know how many myvars are created, much less there exact locations.
Finally: declaring a variable to be const means: "I promise", and does not mean that is a compile time constant. See this example:
int main(int argc, char** argv) {
const int cargc = argc;
char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}
Related
Cppreference states that the expression a defined in
static const int a = std::random_device{}();
is a glvalue constant expression. That would mean that it must also be a core constant expression that is, among other conditions, its evaluation must not involve any calls to functions (or constructors) that aren't declared constexpr as stated here (3rd point). Looking at std::random_device constructors, I'm not seeing any constructor being constexpr.
I checked out the draft on this and I'm not seeing any explicit mention of constructors, but I would say it is implied here:
— an invocation of a non-constexpr function;
Am I missing something here? On the other hand, why isn't it a prvalue constant expression?
Let's make this example simpler (and probably cppreference should do the same):
int f(); // not constexpr, not even defined
void test() {
static const int a = f();
constexpr const int& ra = a; // OK: a is a glvalue constant expression
constexpr int ia = a; // Error: a is not a prvalue constant expression
}
It's true that f() -- or, in the original example, std::random_device{}() -- is not a constant expression. But we don't need it to be to initialize ra. Because we're not reading a, we're just binding a reference to it -- all we need to bind a constexpr reference to a is for a to have static storage duration, which it does. So that's fine.
But reading a as a constant is not allowed, that's what the next line is indicating: we can't initialize ia because a isn't a prvalue constant expression (specificaly, we're violating the rule that we can't apply an lvalue-to-rvalue conversion becuase a isn't usable in constant expressions, which is currently [expr.const]/5.9).
That's basically the distinction between ra and ia: ra just needs a to have a stable address, because that's the part that we need to be constant. But ia needs a to have a constant value. This might be even more obvious if we did:
void test()
{
static const int a = f();
constexpr const int& ra = a; // OK
constexpr const int* pa = &a; // OK
constexpr int ia = a; // Error
}
ra and pa are equivalent - the only thing we care about is the address of a, not the value of a. The value of a isn't constant, but the address is, so it works.
I am learning about constexpr variables using the books listed here. In particular I read in C++ Primer that:
Variables declared constexpr are implicitly const and must be initialized with constant expressions.
Now, to further clear my concept and check if I understood the things correctly, I wrote the following simple program that compiles with msvc but not with gcc and clang:
Demo
int main(void)
{
int i = 0;
constexpr int *ptr= &i; //compiles with msvc but not with clang and gcc
}
So, my question is which compiler is right here(if any)?
The program is ill-formed and msvc is wrong in accepting the code. This is because i is not an statically allocated object and so its address is not constant. Basically, since we can initialize a constexpr pointer only from either the nullptr literal or literal 0 or from an address of an object that has fixed address and in your example, i is not an object that has a fixed addres so that &i is not a constant expression.
This can be seen from expr.const#11 which states:
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 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,
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.
(emphasis mine)
This means that there are two ways to solve the problem. First is that you can add static infront of i to make it a local static. The second is to move the definition of i to global scope outside the main function. In both cases(shown below), i will then have static storage duration so that &i will now be a constant expression according to the above quoted statement.
Method 1
int main()
{
//-vvvvvv------------->static added here so that i now has static storage duration
static int i = 0;
constexpr int *ptr= &i; //works now
}
Method 2
int i = 0; //make i global so that i now has static storage duration
int main()
{
constexpr int *ptr= &i; //works now
}
Here is the msvc bug report:
MSVC compiles invalid constexpr int*ptr= &i where i is a local nonstatic object
I was trying to figure out the restrictions of constexpr in cpp11/14. There are some usage requirements I found in CPP14-5.19-4:
A constant expression is either a glvalue core constant expression
whose value refers to an object with static storage duration or to a
function, or a prvalue core constant expression whose value is an
object where, for that object and its subobjects:
...
if the object or subobject is of pointer type, it contains the address of another object with static storage duration, the address past
the end of such an object (5.7), the address of a function, or
a null pointer value.
I've run some tests(code shown below) for expressions that involves address-of operator &, in order to ensure the correctness of the standards' statements quoted above.
Simply put, I tried to take the address of a global int variable global_var, which is an object with static storage duration(if I was not thinking wrong), everything works just as standards points out. But, what confused me is that, when I tried to assign another pointer-type object(global_var_addr1 in code), which stored the address of the same object global_var, the program won't compile. And GCC says:
error: the value of ‘global_var_addr1’ is not usable in a constant expression
note: ‘global_var_addr1’ was not declared ‘constexpr’
, while Clang-Tidy says:
error: constexpr variable 'x2' must be initialized by a constant expression [clang-diagnostic-error]
note: read of non-constexpr variable 'global_var_addr1' is not allowed in a constant expression
and I don't know why, is there anything I missed?
So my question is:
1. Why, in a constant expression, I cannot use a pointer-type object which contains the address of an object with static storage duration, as standards says?
2. Why everything goes different in the same context as (1), when the object is auto specified?
Any advices would be welcomed, thanks in advance!
Code:
const int global_var_c = 123;
int global_var = 123;
const void *global_var_addr1 = &global_var;
const void *global_var_addr2 = nullptr;
auto global_var_addr3 = nullptr;
auto main() -> int
{
constexpr const int x00 = global_var_c; // OK
constexpr const void *x0 = &global_var; // OK
// Operate on the object of pointer type
constexpr const void *x1 = &global_var_addr1; // OK
constexpr const void *x2 = global_var_addr1; // ERROR: read of non-constexpr variable 'global_var_addr1'...
// Operate on nullptr
constexpr const void *x3 = &global_var_addr2; // OK
constexpr const void *x4 = global_var_addr2; // ERROR: read of non-constexpr variable 'global_var_addr2'...
// Operate on nullptr (with type deduction)
constexpr const void *x5 = global_var_addr3; // OK
constexpr const void *x6 = &global_var_addr3; // OK
}
In both
constexpr const void *x2 = global_var_addr1;
and
constexpr const void *x4 = global_var_addr2;
a lvalue-to-rvalue conversion happens from the variable global_var_addr1/global_var_addr2 glvalue to the pointer value they hold. Such a conversion is only allowed if the variable's lifetime began during the evaluation of the constant expression (not the case here) or if it is usable in constant expressions, meaning that it is constexpr (not the case here) or initialized by a constant expression (is the case here) and of reference or const-qualified integral/enumeration type (not the case here).
Therefore the initializers are not constant expressions.
This is different in the case of
constexpr const int x00 = global_var_c;
since global_var_c is of const-qualified integral type.
I am not exactly sure about
constexpr const void *x5 = global_var_addr3; // OK
Intuitively it should work, because the type of nullptr and consequently the deduced type of global_var_addr3 is std::nullptr_t which doesn't need to carry any state, so that a lvalue-to-rvalue conversion wouldn't be necessary. Whether the standard actually guarantees that, I am not sure at the moment.
Reading the current wording (post-C++20 draft), [conv.ptr] specifies only conversion of a null pointer constant (i.e. a prvalue of std::nullptr_t) to another pointer type and [conv.lval] specifically states how the lvalue-to-rvalue conversion of std::nullptr_t produces a null pointer constant. [conv.lval] also clarifies in a note that this conversion doesn't access memory, but I don't think that makes it not a lvalue-to-rvalue conversion given that it still written under that heading.
So it seems to me that strictly reading the standard
constexpr const void *x5 = global_var_addr3; // OK
should be ill-formed (whether global_var_addr3 is const-qualified or not).
Here is an open clang bug for this. There seems to be a link to come internal discussion by the standards committee, which I cannot access.
In any case, the auto placeholder doesn't matter. You could have written std::nullptr_t for it instead directly.
All of these are requirements for being a core constant expression, which is a prerequisite to the requirements you mention in your question.
The variable declared here is clearly not constexpr (nor even const):
const void *global_var_addr1 = &global_var;
And you can't use non-constexpr values to initialize constexpr values. So it's no surprise this fails to compile:
constexpr const void *x2 = global_var_addr1; // ERROR: read of non-constexpr
The address of a non-constexpr value can be used in cases like you've shown, however, but the value stored in a variable and the address of a variable are not the same thing.
I am very well aware that passing directly a const char* as a template non-type parameter is erroneous, since two identical string literals defined in two different translation units may have different addresses (although most of the time the compilers use the same address). There is a trick one may use, see code below:
#include <iostream>
template<const char* msg>
void display()
{
std::cout << msg << std::endl;
}
// need to have external linkage
// so that there are no multiple definitions
extern const char str1[] = "Test 1"; // (1)
// Why is constexpr enough? Does it have external linkage?
constexpr char str2[] = "Test 2"; // (2)
// Why doesn't this work?
extern const char* str3 = "Test 3"; // (3) doesn't work
// using C_PTR_CHAR = const char* const; // (4) doesn't work either
extern constexpr C_PTR_CHAR str4 = "Test 4";
int main()
{
display<str1>(); // (1')
display<str2>(); // (2')
// display<str3>(); // (3') doesn't compile
//display<str4>(); // (4') doesn't compile
}
Basically in (1) we declare and define an array with external linkage, which can then be used as a template parameter in (1'). I understand this very well. However, I don't understand:
Why does the constexpr version (2) work? Does constexpr have external linkage? If not, then defining the same string literal in a different translation unit may lead to duplicate template instantiation.
Why don't (3) and (4) work? It seems perfectly reasonable for me, but the compiler doesn't believe so:
error: 'str3' is not a valid template argument because 'str3' is a variable, not the address of a variable
1. Short answer: It works irrespective of it being declared constexpr, because you're defining an object with static storage duration (that is not a string literal - it stores a copy of the contents of one), and its address is a constant expression. Regarding linkage, str2 has internal linkage, but that's fine - its address can be used as a non-type template argument.
Long answer:
In C++11 and 14, [14.3.2p1] says the following:
A template-argument for a non-type, non-template template-parameter
shall be one of:
[...]
a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal
linkage or a function with external or internal linkage, including
function templates and function template-ids but excluding non-static
class members, expressed (ignoring parentheses) as & id-expression,
where the id-expression is the name of an object or function, except
that the & may be omitted if the name refers to a function or array
and shall be omitted if the corresponding template-parameter is a
reference;
[...]
So, you can use the address of an object with static storage duration, but the object has to be identified by a name with linkage (internal or external), and the way you're expressing that address is restricted. (String literals are not names and don't have linkage.)
In short, even char str1[] = "Test 1"; works. static char str1[] = "Test 1"; is fine as well; GCC 5.1.0 rejects it, but I think that's a bug; Clang 3.6.0 accepts it.
About str2's linkage, C++11 and 14 [3.5p3] says:
A name having namespace scope (3.3.6) has internal linkage if
it is the name of
[...]
a non-volatile variable that is explicitly declared const or constexpr and neither explicitly declared extern nor previously
declared to have external linkage;
[...]
N4431 has changed that slightly, as a result of DR 1686, to:
a variable of non-volatile const-qualified type that is neither explicitly declared extern nor previously declared to have external
linkage;
reflecting the fact that constexpr implies const-qualification for objects.
2. Short answer: For C++11 and 14, see above; for draft C++1z, str3 is not a constant expression, as the pointer itself is not constexpr, and it's also the address of a string literal. str4 is constant, but still an address of a string literal.
Long answer:
In the current working draft, N4431, the constraints on non-type template arguments have been relaxed. [14.3.2p1] now says:
A template-argument for a non-type template-parameter shall be a
converted constant expression (5.20) of the type of the
template-parameter. For a non-type template-parameter of reference or
pointer type, the value of the constant expression shall not refer to
(or for a pointer type, shall not be the address of):
a subobject (1.8),
a temporary object (12.2),
a string literal (2.13.5),
the result of a typeid expression (5.2.8), or
a predefined __func__ variable (8.4.1).
And those are all the restrictions. The converted constant expression part is pretty important; the full definition is long, but one part relevant to our case is that the address of an object with static storage duration is such an expression.
Also relevant is that, according to [5.20p2.7], an lvalue-to-rvalue conversion applied to
a non-volatile glvalue that refers to a non-volatile object defined
with constexpr, or that refers to a non-mutable sub-object of such an
object
also satisfies the conditions for being a constant expression. This allows us to use some constexpr pointer variables as non-type template arguments. (Note that simply declaring a variable const is not enough, as it can be initialized with a non-constant expression.)
So, something like constexpr const char* str3 = str1; is fine. It's accepted by Clang 3.6.0 in C++1z mode (and rejected in C++14 mode); GCC 5.1.0 still rejects it - it looks like it hasn't implemented the updated rules yet.
Still, what's wrong with string literals? Here's the problem (N4431 [2.13.5p16]):
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.
An implementation is allowed to do lots of things with string literals: mix, match, make them overlap (entirely or partially), make 7 copies from the same translation unit - whatever. That makes the address of a string literal unusable as a non-type template argument.
Why does this work?
char __nontype[] = "foo";
typedef TemplateClass<T, __nontype> MyClass;
But this (with a constant variable) not?
const char __nontype[] = "foo";
typedef TemplateClass<T, __nontype> MyClass;
Compiler Error:
error: ‘__nontype’ cannot appear in a constant-expression
error: template argument 2 is invalid
The difference is because const affects the linkage. It works if you add extern. That said, as far as I can tell:
14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
an integral constant expression (including a constant expression of literal class type that can be used as an integral constant expression as described in 5.19); or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
it should also work without extern. The object is allowed to have internal linkage, but your compiler does not yet support that. This is one of the changes in C++11, the previous C++ standard did not allow it.
The error says it: the result is not a constant expression (it is known at link time, but not compile time).
Here is an example that would work:
typedef const char *nontype_t;
template <nontype_t> struct Y {};
char hello[] = "hello";
constexpr char* world = hello;
int main()
{
Y<hello> a;
}