I'm getting into C++11 and really can't understand why this happens:
const int arrSource[4] = { 5,7,6,4 };
for (auto& i : arrSource) {
std::cout << i << " ";
++i; //error
}
It says that i must be a modifiable lvalue and i: you cannot assign to a variable that is const.
So it means, that if the arrSource[] is const, it makes i const too?
So it means, that if the arrSource[] is const, it makes i const too?
Yes, if the array is const, each element in the array is also const.
The auto& deduces the type based on the initialiser, in this case it is deduced to be int const& and hence cannot be modified.
The increment is probably not needed (not sure on your intent). The range based for loop takes care of the incrementing between iterations.
If modification of the array is intended (via i), then you need to remove the const.
N4567 § 3.9.3 [basic.type.qualifier] p6
Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T”, where
T is an array type, refers to an array whose elements are so-qualified. An array type whose elements are
cv-qualified is also considered to have the same cv-qualifications as its elements.
Related
I'm having some trouble understanding how pointer dereferencing in C++ works. Let's look at this simple example:
struct Value {
int x = 0;
void Inc() { x++; }
};
int main(int argc, char* argv[]) {
Value* v = new Value();
v->Inc();
std::cout << v->x << std::endl; // prints 1, as I would expect
(*v).Inc();
std::cout << v->x << std::endl; // prints 2, but I would have expected it to print 1,
// as I thought (*v) would create a local copy of
// the original `Value` object.
Value v2 = *v;
v2.Inc();
std::cout << v->x << std::endl; // prints 2, as I would expect
I'm a bit confused here. I would assume that the 2nd and 3rd calls to Inc() would be equivalent. Namely, that (*v).Inc() would unfold into a temporary variable holding a copy of v on the stack, and that Inc() would then increment that copy on the stack of v instead of the original v. Why is that not the case?
Thanks
In the (*v).Inc(); statement, the LHS of the . operator is the result of the indirection of the v pointer. This will be an lvalue expression referring to the object to which v points. From this Draft C++ Standard (emphasis mine):
8.5.2.1 Unary operators [expr.unary.op]
1 The unary *
operator performs indirection: the expression to which it is applied
shall be a pointer to an object type, or a pointer to a function type
and the result is an lvalue referring to the object or function to
which the expression points.
So, in this first case, no temporary object need be created and the Inc() function is called on the original Value object created by the new operation.
However, in this statement: Value v2 = *v;, you are declaring a separate Value object and initialising it with a copy of the Value pointed to by v. Thus, any subsequent modifications to v2 will not affect the object referred to by v.
*pointer just returns an object the pointer points to, quoting [expr.unary.op]/1:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points
Value v2 = *v is a form of initialisation, so it actually calls a constructor. This would be equivalent to Value v2{ *v } (for this particular class).
For the part why *pointer doesn't create a temporary, there are well-defined rules on when temporaries are created:
Temporary objects are created when a prvalue is materialized so that
it can be used as a glvalue, which occurs (since C++17) in the
following situations:
binding a reference to a prvalue
initializing an object of type
std::initializer_list from a braced-init-list (since C++11)
returning a prvalue from a function
conversion that creates a prvalue
(including T(a,b,c) and T{})
lambda expression (since C++11)
copy-initialization that requires conversion of the initializer,
reference-initialization to a different but convertible type or to a
bitfield.
plus some others scenarios for C++17. For this particular case the most important part is that indirection returns an lvalue, so there is no rule applicable to it if the expression doesn't partake in any other expression.
When creating a reference in C++ why does the compiler request a value and not an address.
For example:
int i;
int &j = i;
is valid.
int i;
int &j = *&i;
is valid.
int i;
int &j = &i;
is incorrect. If you are equating the address, why does it request a value?
A reference is an alias to an already-existing object or function. In particular case, this is an lvalue reference, so it is a reference to an lvalue, the address of an object is not an lvalue.
A reference to T can be initialized with an object of type T, a
function of type T, or an object implicitly convertible to T.
Considering this, the error we get, when try this says for itself:
invalid conversion from ‘int*’ to ‘int’ // from the &i to the &i
But for example, we can do this:
int i;
int *&&j = &i; // j is an rvalue reference to pointer to int
Also, as was noted in the comments, you should not confuse the & when it is used in a declaration, and when it is used as an operator (i.e. the address-of-operator).
You may benefit from looking at the documentation:
Reference Initialization and Reference declaration.
why does the compiler request a value
It doesn't request a value. You have the wrong frame of mind. All it needs is some expression that identifies an object. The variable name i is such an expression.
In some contexts, expressions that designate object cause an access to a stored value (for instance i = i + i; would incur an access). But not every use of i will cause its value to be read. So for instance when references are bound to it, i doesn't need to be (and indeed isn't) accessed for its value.
In the same vain, *&i doesn't always access the value of i. It's an expression, where the C++ standard says that * and & "collapse" here to simply designating i (with some minutia), but no access happens just due to using the operators themselves.
Hi I am working with 2D and 1D arrays in C++ and using range for to access elements.The code that i am using is as follows:
For 2D Arrays
int ia[3][4] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
for (auto& row : ia) {
for (auto col : row) {
cout << col << endl;
}
}
For ID Array
int base_array[2] = { 1, 2 };
for (auto p : base_array) {
std::cout << p << std::endl;
}
So as we can see in the outside range for loop of 2D array, the control variable row must be of reference type so that the automatic array to pointer conversion does not happen and we can loop through the elements which themselves are arrays of size 4. But in the 1D array we are not using the control variable p as a reference type and even then we are able to traverse through the 1D array. My question is why in the 1D array the array to pointer conversion is not taking place? How is it working without the control variable being of reference type? That is, why (how) is p of int type instead of int* in 1D array case while if we omit the reference symbol in the control variable row it will then become a int* but when we include the reference symbol in front of variable row then it will become an array type. Why there is this difference in behavior?
In your 1D case, base_array isn't decayed because it exists in the same scope as the for loop. So the range-based for can correctly figure out its range. That's really it, the 1D isn't decayed. If you had tried passing that 1D array through a function, you'd see that the range-based for becomes impossible because it will decay, unless you passed it as a reference.
In your 2D case, if you allow row to decay to int*, the nested for can no longer assume you must mean an array. So, you preserve the type by using auto&, allowing the nested for to know what you're talking about.
why in the 1D array the array to pointer conversion is not taking place?
In the case of 1D array, the elements of the array are int objects whereas in the case of 2D array, the elements of the array are "array of int" objects.
Without using a reference, an array of int will decay to a pointer. Hence, it is appropriate to use a reference for the first case.
Case 1(Without reference symbol infront of row)
For the outside loop, the loop variable row is initialized to the first element of ia which is itself an array, so array to pointer conversion will take place and row will become int*. And since we cannot loop through an int*, we get an error when we try to do so in the inner for loop.
Case 2(With reference symbol infront of row)
When we add reference symbol infont of row, then that array to pointer conversion will no longer take place and row will be an array type and not a pointer type and since we can loop through an array , hence this case will work.
From the C++ 17 Standard (10.1.7.4.1 Placeholder type deduction)
4 If the placeholder is the auto type-specifier, the deduced type T0
replacing T is determined using the rules for template argument
deduction. Obtain P from T by replacing the occurrences of auto with
either a new invented type template parameter U or, if the
initialization is copy-list-initialization, with
std::initializer_list. Deduce a value for U using the rules of template argument
deduction from a function call (17.8.2.1)
and (17.8.2.1 Deducing template arguments from a function call)
2 If P is not a reference type:
(2.1) — If A is an array type, the pointer type produced by the
array-to-pointer standard conversion (7.2) is used in place of A for
type deduction; otherwise,
Thus if you will not use a referenced type in the loop
for (auto row : ia)
then the type of the variable row will be int * that is it will be a pointer type for which you can not use the range based for loop.
I was reading about strict aliasing, but its still kinda foggy and I am never sure where is the line of defined / undefined behaviour. The most detailed post i found concentrates on C. So it would be nice if you could tell me if this is allowed and what has changed since C++98/11/...
#include <iostream>
#include <cstring>
template <typename T> T transform(T t);
struct my_buffer {
char data[128];
unsigned pos;
my_buffer() : pos(0) {}
void rewind() { pos = 0; }
template <typename T> void push_via_pointer_cast(const T& t) {
*reinterpret_cast<T*>(&data[pos]) = transform(t);
pos += sizeof(T);
}
template <typename T> void pop_via_pointer_cast(T& t) {
t = transform( *reinterpret_cast<T*>(&data[pos]) );
pos += sizeof(T);
}
};
// actually do some real transformation here (and actually also needs an inverse)
// ie this restricts allowed types for T
template<> int transform<int>(int x) { return x; }
template<> double transform<double>(double x) { return x; }
int main() {
my_buffer b;
b.push_via_pointer_cast(1);
b.push_via_pointer_cast(2.0);
b.rewind();
int x;
double y;
b.pop_via_pointer_cast(x);
b.pop_via_pointer_cast(y);
std::cout << x << " " << y << '\n';
}
Please dont pay too much attention to a possible out-of-bounds access and the fact that maybe there is no need to write something like that. I know that char* is allowed to point to anything, but I also have a T* that points to a char*. And maybe there is something else I am missing.
Here is a complete example also including push/pop via memcpy, which afaik isn't affected by strict aliasing.
TL;DR: Does the above code exhibit undefined behaviour (neglecting a out-of-bound acces for the moment), if yes, why? Did anything change with C++11 or one of the newer standards?
Aliasing is a situation when two entities refer to the same object. It may be either references or pointers.
int x;
int* p = &x;
int& r = x;
// aliases: x, r и *p refer to same object.
It's important for compiler to expect that if a value was written using one name it would be accessible through another.
int foo(int* a, int* b) {
*a = 0;
*b = 1;
return *a;
// *a might be 0, might be 1, if b points at same object.
// Compiler can't short-circuit this to "return 0;"
}
Now if pointers are of unrelated types, there is no reason for compiler to expect that they point at same address. This is the simplest UB:
int foo( float *f, int *i ) {
*i = 1;
*f = 0.f;
return *i;
}
int main() {
int a = 0;
std::cout << a << std::endl;
int x = foo(reinterpret_cast<float*>(&a), &a);
std::cout << a << "\n";
std::cout << x << "\n"; // Surprise?
}
// Output 0 0 0 or 0 0 1 , depending on optimization.
Simply put, strict aliasing means that compiler expects names of unrelated types refer to object of different type, thus located in separate storage units. Because addresses used to access those storage units are de-facto same, result of accessing stored value is undefined and usually depends on optimization flags.
memcpy() circumvents that by taking the address, by pointer to char, and makes copy of data stored, within code of library function.
Strict aliasing applies to union members, which described separately, but reason is same: writing to one member of union doesn't guarantee the values of other members to change. That doesn't apply to shared fields in beginning of struct stored within union. Thus, type punning by union is prohibited. (Most compilers do not honor this for historical reasons and convenience of maintaining legacy code.)
From 2017 Standard: 6.10 Lvalues and rvalues
8 If a program attempts to access the stored value of an object
through a glvalue of other than one of the following types the
behavior is undefined
(8.1) — the dynamic type of the object,
(8.2) — a cv-qualified version of the dynamic type of the object,
(8.3) — a type similar (as defined in 7.5) to the dynamic type of the
object,
(8.4) — a type that is the signed or unsigned type corresponding to
the dynamic type of the object,
(8.5) — a type that is the signed or unsigned type corresponding to a
cv-qualified version of the dynamic type of the object,
(8.6) — an aggregate or union type that includes one of the
aforementioned types among its elements or nonstatic data members
(including, recursively, an element or non-static data member of a
subaggregate or contained union),
(8.7) — a type that is a (possibly cv-qualified) base class type of
the dynamic type of the object,
(8.8) — a char, unsigned char, or std::byte type.
In 7.5
1 A cv-decomposition of a type T is a sequence of cvi and Pi such that T is “cv0 P0 cv1 P1 · · · cvn−1 Pn−1 cvn U” for n > 0, where each
cvi is a set of cv-qualifiers (6.9.3), and each Pi is “pointer to”
(11.3.1), “pointer to member of class Ci of type” (11.3.3), “array of
Ni”, or “array of unknown bound of” (11.3.4). If Pi designates an
array, the cv-qualifiers cvi+1 on the element type are also taken as
the cv-qualifiers cvi of the array. [ Example: The type denoted by the
type-id const int ** has two cv-decompositions, taking U as “int” and
as “pointer to const int”. —end example ] The n-tuple of cv-qualifiers
after the first one in the longest cv-decomposition of T, that is,
cv1, cv2, . . . , cvn, is called the cv-qualification signature of T.
2 Two types T1 and T2 are similar if they have cv-decompositions with
the same n such that corresponding Pi components are the same and the
types denoted by U are the same.
Outcome is: while you can reinterpret_cast the pointer to a different, unrelated and not similar type, you can't use that pointer to access stored value:
char* pc = new char[100]{1,2,3,4,5,6,7,8,9,10}; // Note, initialized.
int* pi = reinterpret_cast<int*>(pc); // no problem.
int i = *pi; // UB
char* pc2 = reinterpret_cast<char*>(pi+2); // *(pi+2) would be UB
char c = *pc2; // no problem, unless increment didn't put us beyond array bound.
// c equals to 9
'reinterpret_cast' doesn't create objects. To dereference a pointer at a non-existing object is Undefined Behaviour, so you can't use dereferenced result of cast for writing if class it points to wasn't trivial.
I know that char* is allowed to point to anything, but I also have a T* that points to a char*.
Right, and that is a problem. While the pointer cast itself has defined behaviour, using it to access a non-existent object of type T is not.
Unlike C, C++ does not allow impromptu creation of objects*. You cannot simply assign to some memory location as type T and have an object of that type be created, you need an object of that type to be there already. This requires placement new. Previous standards were ambiguous on it, but currently, per [intro.object]:
1 [...] An object is created by a definition (6.1), by a new-expression (8.3.4), when implicitly changing the active member of a union (12.3), or when a temporary object is created (7.4, 15.2). [...]
Since you are not doing any of these things, no object is created.
Furthermore, C++ does not implicitly consider pointers to different object at the same address as equivalent. Your &data[pos] computes a pointer to a char object. Casting it to T* does not make it point to any T object residing at that address, and dereferencing that pointer has undefined behaviour. C++17 adds std::launder, which is a way to let the compiler know that you want to access a different object at that address than what you have a pointer to.
When you modify your code to use placement new and std::launder, and ensure you have no misaligned accesses (I presume you left that out for brevity), your code will have defined behaviour.
* There is discussion on allowing this in a future version of C++.
Short answer:
You may not do this: *reinterpret_cast<T*>(&data[pos]) = until there has been an object of type T constructed at the pointed-to address. Which you can accomplish by placement new.
Even then, you might need to use std::launder as for C++17 and later, since you access the created object (of type T) through a pointer &data[pos] of type char*.
"Direct" reinterpret_cast is allowed only in some special cases, e.g., when T is std::byte, char, or unsigned char.
Before C++17 I would use the memcpy-based solution. Compiler will likely optimize away any unnecessary copies.
Why do constant references not behave the same way as constant pointers, so that I can actually change the object they are pointing to? They really seem like another plain variable declaration. Why would I ever use them?
This is a short example that I run which compiles and runs with no errors:
int main (){
int i=0;
int y=1;
int&const icr=i;
icr=y; // Can change the object it is pointing to so it's not like a const pointer...
icr=99; // Can assign another value but the value is not assigned to y...
int x=9;
icr=x;
cout<<"icr: "<<icr<<", y:"<<y<<endl;
}
The clearest answer.
Does “X& const x” make any sense?
No, it is nonsense
To find out what the above declaration means, read it right-to-left:
“x is a const reference to a X”. But that is redundant — references
are always const, in the sense that you can never reseat a reference
to make it refer to a different object. Never. With or without the
const.
In other words, “X& const x” is functionally equivalent to “X& x”.
Since you’re gaining nothing by adding the const after the &, you
shouldn’t add it: it will confuse people — the const will make some
people think that the X is const, as if you had said “const X& x”.
The statement icr=y; does not make the reference refer to y; it assigns the value of y to the variable that icr refers to, i.
References are inherently const, that is you can't change what they refer to. There are 'const references' which are really 'references to const', that is you can't change the value of the object they refer to. They are declared const int& or int const& rather than int& const though.
What is a constant reference (not a reference to a constant)
A Constant Reference is actually a Reference to a Constant.
A constant reference/ Reference to a constant is denoted by:
int const &i = j; //or Alternatively
const int &i = j;
i = 1; //Compilation Error
It basically means, you cannot modify the value of type object to which the Reference Refers.
For Example:
Trying to modify value(assign 1) of variable j through const reference, i will results in error:
assignment of read-only reference ‘i’
icr=y; // Can change the object it is pointing to so it's not like a const pointer...
icr=99;
Doesn't change the reference, it assigns the value of the type to which the reference refers.
References cannot be made to refer any other variable than the one they are bound to at Initialization.
First statement assigns the value y to i
Second statement assigns the value 99 to i
This code is ill-formed:
int&const icr=i;
Reference: C++17 [dcl.ref]/1:
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced
through the use of a typedef-name or decltype-specifier, in which case the cv-qualifiers are ignored.
This rule has been present in all standardized versions of C++. Because the code is ill-formed:
you should not use it, and
there is no associated behaviour.
The compiler should reject the program; and if it doesn't, the executable's behaviour is completely undefined.
NB: Not sure how none of the other answers mentioned this yet... nobody's got access to a compiler?
By "constant reference" I am guessing you really mean "reference to constant data". Pointers on the other hand, can be a constant pointer (the pointer itself is constant, not the data it points to), a pointer to constant data, or both.
As it mentioned in another answers, a reference is inherently const.
int &ref = obj;
Once you initialized a reference with an object, you can't unbound this reference with its object it refers to. A reference works just like an alias.
When you declare a const reference, it is nothing but a reference which refers to a const object.
const int &ref = obj;
The declarative sentences above like const and int is determining the available features of the object which will be referenced by the reference. To be more clear, I want to show you the pointer equivalent of a const reference;
const int *const ptr = &obj;
So the above line of code is equivalent to a const reference in its working way. Additionally, there is a one last point which I want to mention;
A reference must be initialized only with an object
So when you do this, you are going to get an error;
int &r = 0; // Error: a nonconst reference cannot be initialized to a literal
This rule has one exception. If the reference is declared as const, then you can initialize it with literals as well;
const int &r = 0; // a valid approach
First I think int&const icr=i; is just int& icr = i, Modifier 'const' makes no sense(It just means you cannot make the reference refer to other variable).
const int x = 10;
// int& const y = x; // Compiler error here
Second, constant reference just means you cannot change the value of variable through reference.
const int x = 10;
const int& y = x;
//y = 20; // Compiler error here
Third, Constant references can bind right-value. Compiler will create a temp variable to bind the reference.
float x = 10;
const int& y = x;
const int& z = y + 10;
cout << (long long)&x << endl; //print 348791766212
cout << (long long)&y << endl; //print 348791766276
cout << (long long)&z << endl; //print 348791766340