# Variables and Basic Types

C++ keywords & alternative operator names:

```
alignas alignof and and_eq asm 
auto bitand bitor bool break
case catch char char8_t char16_t
char32_t class compl concept const 
const_cast consteval constexpr constinit continue 
co_await co_return co_yield decltype default
delete do double dynamic_cast else 
enum explicit export extern false
float for friend goto if 
inline int long mutable namespace 
new noexcept not not_eq nullptr 
operator or or_eq private protected 
public register reinterpret_cast requires return 
short signed sizeof static static_assert
static_cast struct switch template this
thread_local throw true try typedef 
typeid typename union unsigned using 
declaration using directive virtual void 
volatile wchar_t while xor xor_eq
```

## Compound Type

A declaration is a **base type** followed by a list of **declarators**.

### References

When we define a reference, we **bind** the reference to its initializer. There is no way to rebind a reference to refer to a different object.

```c++
int i = 0;
int& r1 = i; // Legal but might be misleading.The declarator should be put with the variable name.
int &r2 = i; // Good.
```

Two exceptions to the rule that **the type of a reference** must match **the type of the object to which it refers**:

* We can initialize a reference to const from any expression that can be converted

### Pointers

**Null pointers**:

```c++
int *p1 = nullptr; // Equivalent to int *p1 = 0;
int *p2 = 0; // Directly initializes p2 from the literal constant 0.

int *p3 = NULL; // Equivalent to int *p3 = 0; Must #include <cstdlib>
```

A `void*` pointer holds an address, but the type of the object at that address is unknown.

### Discussion

```c++
// i is an int; p is a pointer to int; r is a reference to int 
int i = 1024, *p = &i, &r = i;
```

A reference is not an object such that we may not have a pointer to a reference.

A pointer is an object, we can define a reference to a pointer.

```c++
int *p; // p is a pointer to int
int *&r = p; // r is a reference to the pointer p
```

**Suggestion**: understand pointer or reference declarations by reading from right to left.

## `const` Qualifier

**Because we can’t change the value of a `const` object after we create it, it must be initialized.**

By default, `const` objects are local to a file.

To define a single instance of a **const variable** that can be shared acrossed files, we use the keyword `extern` on both its definition and declaration(s):

```c++
// In file_1.cc defines and initializes a const that is accessible to other files.
extern const int bufSize = fcn();
// In file_1.h
extern const int bufSize; // Same bufSize as defined in file_1.cc
```

### **References to** `const`

```c++
const int ci = 1024;
const int &r1 =  ci; // OK: both reference and underlying object are const
r1 = 42; // ERROR: r1 is a reference to const
int &r2 = ci; // ERROR: nonconst reference to a const object
```

Because we cannot assign directly to `ci`, we also should not be able to use a reference to change ci.

> reference to const == const reference

We can initialize a reference to const from any expression that can be converted

```c++
double dval = 3.14;
const int &ri = dval;
```

The code is transdformed by the compiler into:

```c
const int temp = dval;
const int &ri = temp;
```

A reference to const **restricts only what we can do** through that reference. Binding a reference to `const` to an object says nothing about whether the underlying object itself is `const`.

### **Pointers and** `const`

```c++
const double pi = 3.14; // pi is const; its value may not be changed 
double *ptr = &pi; // Error: ptr is a plain pointer
const double *cptr = &pi; // Ok:cptr may point to a double that is const
*cptr = 42; // Error: cannot assign to *cptr
```

**`const` Pointers**

> const pointer != pointer to const

Like any other const object, a **const** **pointer** must be initialized, and once initialized, its value (i.e., the address that it holds) may not be changed.

```c++
int errNumb = 0;
int *const curErr = &errNumb; // curErr will always point to errNumb 
const double pi = 3.14159;
const double *const pip = &pi; // pip is a const pointer to a const object
```

### Top-Level `const`

We use the term **top-level** **const** to indicate that the pointer itself is a const. When a pointer can point to a const object, we refer to that const as a **low-level** **const**.

```c++
const int *p1; // This is a fucking low-level const.
int const* p2; // This is a fucking top-level const.
int *const p2; // This is also a fucking top-level const.
```

### `constexpr` and Constant Expressions

A **constant expression** is an expression whose value cannot change and that can be evaluated at compile time.

```c++
const int max_files = 20; // max_files is a constant expression
const int limit = max_files + 1; // limit is a constant expression
int staff_size = 27; // staff_size is not a constant expression
const int sz = get_size(); // sz is not a constant expression
```

Under the new standard, we can ask the compiler to verify that a variable is a constant expression by declaring the variable in a **constexpr** declaration. Variables declared as `constexpr` are implicitly const and must be initialized by constant expressions.

```c++
constexpr int mf = 20; // 20 is a constant expression
constexpr int limit = mf + 1; // mf + 1 is a constant expression 
constexpr int sz = size(); // OK only if size is a constexpr function
```

The types we can use in a constexpr are known as “literal types” because they are simple enough to have literal values.

We can initialize a `constexpr` pointer from the `nullptr` literal or the literal (i.e., constant expression) `0`. We can also point to (or bind to) an object that remains at **a fixed address** (an object defined outside of any function).

```c++
constexpr int *np = nullptr; // np is a constant pointer to int that is null 
int j = 0;
constexpr int i = 42; // type of i is const int
// i and j must be defined outside any function
constexpr const int *p = &i; // p is a constant pointer to the const int i 
constexpr int *p1 = &j; // p1 is a constant pointer to the int j
```

***

It is important to understand that when we define a pointer in a `constexpr` declaration, the `constexpr` specifier applies to the pointer, not the type to which the pointer points:

```c++
const int *p = nullptr; // p is a pointer to a const int
constexpr int *q = nullptr; // q is a const pointer to int
```

## Dealing with Types

### Type Aliases

```c++
typedef double wages; // wages is a synonym for double
typedef wages base, *p; // base is a synonym for double, p for double*
```

The new standard introduced a second way to define a type alias, via an **alias declaration**:

```c++
using SI = Sales_item; // SI is a synonym for Sales_item
```

\*\*Do not intepreting a declaration that uses a type alias by conceptually replacing the alias with its corresponding type. \*\*

```c++
typedef char *pstring;
const pstring cstr = 0; // cstr is a constant pointer to char, char * is the base type
const char *cstr=0; // the base type is const char
```

### **The** **auto** Type Specifier

```c++
auto i = 0, *p = &i; // ok:i is int and p is a pointer toint
```

**Compound Types, `const` and `auto`**

```c++
int i = 0, &r = i;
auto a = r; // a is an int(r is an alias fori, which has type int)
```

`auto` ordinarily ignores top-level consts.

```c++
const int ci = i, &cr = ci;
auto b = ci; // b is an int (top-level const in ci is dropped)
auto c = cr; // c is an int (cr is an alias for ci whose const is top-level) 
auto d = &i; // d is an int*(& of an int object is int*)
auto e = &ci; // e is const int* (& of a const object is low-level const)
```

If we want the deduced type to have a top-level const, we must say so explicitly:

```c++
const auto f=ci; // deduced type of ci is int;f has type const int
```

We can also specify that we want a reference to the auto-deduced type. When we ask for a reference to an auto-deduced type, top-level consts in the initializer are not ignored.

```c++
auto &g = ci; // g is a const int& that is bound to ci 
auto &h = 42; // error: we can’t bind a plain reference to a literal 
const auto &j = 42; // ok: we can bind a const reference to a literal
```

### **The** `decltype` Type Specifier

```c++
decltype(f()) sum = x; // sum has whatever type f returns
```

Here, the compiler does not call f, but it uses the type that such a call would return as the type for sum.

When the expression to which we apply decltype is a variable, decltype returns the type of that variable, including top-level const and reference:

```c++
const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x has type const int
decltype(cj) y = x; // y has type const int& and is bound to x
decltype(cj) z; // error: z is a reference and must be initialized
```

`decltype` **and References**

Generally speaking, `decltype` returns a reference type for **expressions** that yield **objects that can stand on the left-hand side of the assignment**.

***The above rule only apply to EXPRESSIONs. For example,****&#x20;****`i`****&#x20;****is an variable while****&#x20;****`i+0`****&#x20;****and****&#x20;****`(i)`\*\*\*\* \*\*\*\*are expressions.***

```c++
// decltype of an expression can be a reference type
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; // ok: addition yields an int; b is an (uninitialized) int 
decltype(*p) c; // error: c is int& and must be initialized
```

The dereference operator is an example of an expression for which decltype returns a reference.
