Is pointer type a literal type? - c++

A scalar type is defined as
Trait class that identifies whether T is a scalar type. A scalar type
is a type that has built-in functionality for the addition operator
without overloads (arithmetic, pointer, member pointer, enum and
std::nullptr_t).
It inherits from integral_constant as being either true_type or
false_type, depending on whether T is a scalar type, no matter its
const and/or volative qualification.
It means pointer is scalar type.
Now if we go to definition of literal type:
A type is a literal type if it is:
a scalar type; or
a reference type; or
an array of literal type; or
-a class type (Clause 9) that has all of the following properties:
it has a trivial destructor,
every constructor call and full-expression in the brace-or-equal-initializers for non-static data members (if any) is a constant expression (5.19),
it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
all of its non-static data members and base classes are of literal types.
Now, combining above 2 statements, it means pointer is literal type. However pointer can not be constexpr. can someone please clarify?
further see following code:
int a = 7;
constexpr int *pointer1 = &a;
int main ()
{
int b = 4;
constexpr int *pointer2 = &b;
}
pointer1 is fine but pointer 2 gives error. does that mean pointer to global is fine but to automatic variable is not? Does standard mention this anywhere ?

Pointers are literal types. They can be constexpr under certain conditions:
[expr.const] 6
... [a pointer is constexpr if] it contains the address of an 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.
(Where "object with static storage duration" means a global or static object, or a subobject of such object.)
A demo:
int x;
int main()
{
constexpr int *ptr = &x; // Compiles.
// Doesn't compile: `error: '& foo' is not a constant expression`
// int foo;
// constexpr int *bar = &foo;
}
Apparently GCC (with -pedantic-errors -std=c++11/14/17) happily accepts out-of-range constexpr pointer arithmetic: constexpr int *ptr = &x - 10;, which seems like a bug to me.

Yes they are literals; yes they can be constexpr. To demonstrate:
constexpr int* foo=0;
int x = 7;
constexpr int* ptr=&x;
int main(){}

Related

Is `a` in `static const int a = std::random_device{}();` a glvalue core constant expression?

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.

Weird behaviour constexpr with std::initializer_list

I am trying to understand why the compiler is complaining here:
// cexpr_test.cpp
#include <initializer_list>
constexpr int test_cexpr(std::initializer_list<const char*> x)
{
return (int) (*x.begin())[0]; // ensuring the value isn't optimized out.
}
int main()
{
constexpr int r1 = test_cexpr({ "why does this work," });
constexpr std::initializer_list<const char*> broken { "but this doesn't?" };
constexpr int r2 = test_cexpr(broken);
return r1 + r2;
}
The message produced when compiled with
g++ -std=c++11 -Wall -Werror cexpr_test.cpp
is as follows:
cexpr_test.cpp: In function ‘int main()’:
cexpr_test.cpp:12:76: error: ‘const std::initializer_list{((const char* const*)(&)), 1}’ is not a constant expression
12 | constexpr std::initializer_list broken { "but this doesn't?" };
|
It's confusing why it constructs the first initializer list without any issues. What am I missing here?
The problem is with the initialization of broken itself here. What is a std::initializer_list and what does it hold? It's a reference type (i.e. refers somehow to another objects), and it's backed by a c-style array. The properties of this c-style array are what determines if the initializer_list can be a constexpr variable. We can consult [dcl.init.list] for those properties.
5 An object of type std​::​initializer_­list<E> is constructed
from an initializer list as if the implementation generated and
materialized a prvalue of type “array of N const E”, where N is
the number of elements in the initializer list. Each element of that
array is copy-initialized with the corresponding element of the
initializer list, and the std​::​initializer_­list<E> object is
constructed to refer to that array. [ Note: A constructor or
conversion function selected for the copy shall be accessible in the
context of the initializer list.  — end note ] If a narrowing
conversion is required to initialize any of the elements, the program
is ill-formed. [ Example:
struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
The initialization will be implemented in a way roughly equivalent to
this:
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
assuming that the implementation can construct an initializer_­list
object with a pair of pointers.  — end example ]
6 The array has the same lifetime as any other temporary object,
except that initializing an initializer_­list object from the array
extends the lifetime of the array exactly like binding a reference to
a temporary. [ Example:
typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };
void f() {
std::vector<cmplx> v2{ 1, 2, 3 };
std::initializer_list<int> i3 = { 1, 2, 3 };
}
struct A {
std::initializer_list<int> i4;
A() : i4{ 1, 2, 3 } {} // ill-formed, would create a dangling reference
};
For v1 and v2, the initializer_­list object is a parameter in a
function call, so the array created for { 1, 2, 3 } has
full-expression lifetime. For i3, the initializer_­list object is
a variable, so the array persists for the lifetime of the variable.
For i4, the initializer_­list object is initialized in the
constructor's ctor-initializer as if by binding a temporary array to a
reference member, so the program is ill-formed ([class.base.init]).
 — end example ] [ Note: The implementation is free to allocate the
array in read-only memory if an explicit array with the same
initializer could be so allocated.  — end note ]
So this array is like any other temporary object that is referred to by a constant reference. Which means we can actually reduce your minimal example to something even smaller
constexpr int test_cexpr(int const & x)
{
return x;
}
int main()
{
constexpr int r1 = test_cexpr(0);
constexpr int const &broken = 0;
constexpr int r2 = test_cexpr(broken);
return r1 + r2;
}
This produces the exact same behavior and error. We can pass 0 directly as an argument to the constexpr function, the reference binds, and we can even refer to it inside the function. However, a constexpr reference may not be initialized with 0. The reason for zero not being a valid initializer, is that it requires materializing a temporary int object. This temporary is not a static variable, and so may not be used to initialize a constexpr reference. Simple as that.
The same reasoning applies to your case. The temporary array that's materialized is not an object with static storage duration, so it may not be used to initialize a constexpr reference type.
The reason it works when directly passing the argument to test_cexpr, is that the corresponding parameter is not itself a constexpr variable. Which means it can bind successfully. After which, the thing it's bound to just has to be usable in a constant expression. Without going into too much detail over this: since the temporary in that case has full-expression lifetime (and not lifetime extended), it is usable in a constant expression.

MSVC const enum type

const enum Alpha{
X=9,
Y=5,
Z=2
}p;
int main(){
enum Alpha a,b;
a= X;
b= Z;
p = X;
p = Y;
printf("%d",a+b-p);
return 0;
}
Why is p = X and p = Y allowed in MSVC compiler? This code outputs 6. Shouldn't a const value be assigned at initialization and never again?
That is a bug in the compiler itself. End of the story.
In fact, your little code shows two bugs in the compiler. The first bug is here itself:
const enum Alpha{
X=9,
Y=5,
Z=2
}p; //declaration of p is ill-formed!
The declaration of p is ill-formed, and thus the compiler should reject this code, because p is declared const but left uninitialized. A const scalar (and pod) type must be initialized in order to be well-formed:
const Alpha q; //ill-formed (same case is with p in your code)
const Alpha r = X; //well-formed
For detailed and extensive explanation, see this:
Why do const variables have to be initialized right away?
Looks like a bug, indeed.
First of all, global const objects must be initialized when defined, and default-initialization is not an option for enumeration types. According to Paragraph 8.5/6 of the C++11 Standard:
To default-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the
initialization is ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, no initialization is performed.
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
Secondly, a const object cannot be assigned after initialization.
Edited to match consensus that this is a compiler bug.
This works because the compiler erroneously thinks that p is of type Alpha and not const Alpha. If you rewrite this as
enum Alpha{....
} const p;
the compiler will correctly complain that a constant is not being initialized.
error C2734: 'p' : const object must be initialized if not extern
error C3892: 'p' : you cannot assign to a variable that is const
If you assign the constant,
enum Alpha{....
} const p = Y;
and remove the assignment to p, everything compiles and works as expected.

Why can't a constant pointer be a constant expression?

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.
}

Initializing structs in C++

As an addendum to this question, what is going on here:
#include <string>
using namespace std;
struct A {
string s;
};
int main() {
A a = {0};
}
Obviously, you can't set a std::string to zero. Can someone provide an explanation (backed with references to the C++ Standard, please) about what is actually supposed to happen here? And then explain for example):
int main() {
A a = {42};
}
Are either of these well-defined?
Once again an embarrassing question for me - I always give my structs constructors, so the issue has never arisen before.
Your struct is an aggregate, so the ordinary rules for aggregate initialization work for it. The process is described in 8.5.1. Basically the whole 8.5.1 is dedicated to it, so I don't see the reason to copy the whole thing here. The general idea is virtually the same it was in C, just adapted to C++: you take an initializer from the right, you take a member from the left and you initialize the member with that initializer. According to 8.5/12, this shall be a copy-initialization.
When you do
A a = { 0 };
you are basically copy-initializing a.s with 0, i.e. for a.s it is semantically equivalent to
string s = 0;
The above compiles because std::string is convertible from a const char * pointer. (And it is undefined behavior, since null pointer is not a valid argument in this case.)
Your 42 version will not compile for the very same reason the
string s = 42;
will not compile. 42 is not a null pointer constant, and std::string has no means for conversion from int type.
P.S. Just in case: note that the definition of aggregate in C++ is not recursive (as opposed to the definition of POD, for example). std::string is not an aggregate, but it doesn't change anything for your A. A is still an aggregate.
8.5.1/12 "Aggregates" says:
All implicit type conversions (clause 4) are considered when initializing the aggregate member with an initializer from an initializer-list.
So
A a = {0};
will get initialized with a NULL char* (as AndreyT and Johannes indicated), and
A a = {42};
will fail at compile time since there's no implicit conversion that'll match up with a std::string constructor.
0 is a null pointer constant
S.4.9:
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to
zero.
A null pointer constant can be converted to any other pointer type:
S.4.9:
A null pointer constant can be converted to a pointer type; the result is the null pointer value of that
type
What you gave for the definition of A is considered an aggregate:
S.8.5.1:
An aggregate is an array or a class with no user-declared constructors, no private or protected
non-static data members, no base classes, and no virtual functions.
You are specifying an initializer clause:
S.8.5.1:
When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace enclosed,
comma-separated list of initializer-clauses for the members of the aggregate
A contains a member of the aggregate of type std::string, and the initializer clause applies to it.
Your aggregate is copy-initialized
When an aggregate (whether class or array) contains members of class type and is initialized by a brace enclosed
initializer-list, each such member is copy-initialized.
Copy initializing means that you have the equivalent to std::string s = 0 or std::string s = 42;
S.8.5-12
The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling
an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent
to the form T x = a;
std::string s = 42 will not compile because there is no implicit conversion, std::string s = 0 will compile (because an implicit conversion exists) but results in undefined behavior.
std::string's constructor for const char* is not defined as explicit which means you can do this: std::string s = 0
Just to show that things are actually being copy-initialized, you could do this simple test:
class mystring
{
public:
explicit mystring(const char* p){}
};
struct A {
mystring s;
};
int main()
{
//Won't compile because no implicit conversion exists from const char*
//But simply take off explicit above and everything compiles fine.
A a = {0};
return 0;
}
As people have pointed out, this "works" because string has a constructor that can take 0 as a parameter. If we say:
#include <map>
using namespace std;
struct A {
map <int,int> m;
};
int main() {
A a = {0};
}
then we get a compilation error, as the map class does not have such a constructor.
In 21.3.1/9 the standard forbids the char* argument of the relevant constructor of a std::basic_string from being a null pointer. This should throw a std::logic_error, but I have yet to see where in the standard is the guarantee that violating a precondition throws a std::logic_error.