Misc Info

Created Wednesday 04 May 2016

Miscellaneous information not large enough or important enough to warrant its own page goes here. More important parts are highlighted.]



adding const is an extra step in overload resolution


Template Resolution steps

  1. Scope/Name space lookup
  2. Perform type deduction for each template
  3. Collect all viable functions including function templates


Having a const OR a ref in struct/class means NO default constructor, copy assignment, copy constructor.


  1. Universal Conversion Constructor
  2. SFINAE (Substitution Failure is not an Error)
  3. Type Erasure
  4. Variadic Template
  5. std::function

Promotion char->int, short->int, bool->int

Conversion char->short




C++ Overloading Rules require you to win in every single argument. Otherwise, ambiguous. In addition, arguments are compared ONE BY ONE.



int (*) [10][10] != int * [10][10]

Be careful when answering type questions that don't have a name for the declaration

R(V) is a function declaration for a func that takes in V and returns R. this is useful for other shit cause it explains why some statements cant be made.


Perfect forwarding is basically forwarding lvalue to lvalue, rvalue to rvalue, etc
well theres no etc thats p much it actually


Reference collapsing rules

& & => &
&& & => &
& &&=> &
&& && => &&


template<typename T>
void foo(T&&);

foo(x); // x is lvalue, T is deduced as int &,

//T&& = int & && = int &
foo<int&>(x); //same as above
foo(std::move(x)); //std::move(x) is rvalue, T is deduced as int, T&& becomes int &&
foo<int&&>(std::move(x)); // T is int &&, T && becomes int && && = int &&

when doing rvalue reference shit, set the other shit to nullptr, otherwise there might be dangling pointers!!

class StrCont

Str s;
char * buf;
int mlen;
StrCont(StrCont && rhs)



int x;
int && rrx = std::move(x); //YAY
int && rrx2=rrx; //NAY

rvalue references are NOT rvalues, they contain REFERENCES to rvalues, which negates the primary rules of prvalues and xvalues, ie, they are LVALUES.


You cannot pass lvalue references into rvalue references, unless you use std::move


rvalue references cannot be deduced, ie they cant be in templated FUNCTIONS....

int && a = 5; //A is an rvalue ref.

template <typename T>
class hoho { T&& x };

hoho<Obj> h; //not deduced

template <typename T>
void laugh(T&& b);

laugh (x); //deduced

Laugh takes in a universal reference. Depending on context, it can either be an lvalue ref or rvalue ref. (ie can be lvalue or rvalue depending on the thing passed in)

template <typename T>
class hoho {

template<typename U>
hoho(U&& u)

T&& x;

U&& u is a universal reference in this case.


int & rx_plus_y = x+y; //NAY
const int & thing = x+y; //SURE


The proper term is there are
- pprvalues
- xvalues
rvalues is the union of prvalues and xvalues

- pure r values
- basically values without an identifier
- cannot take address of
e.g. 3+x, &(3+x) (can't be done)
- usually not allowed to assign to
e.g. 3+x =10;

- expiring values (values that are about to die)
- usually refers to values returned by functions

int foo();
Can I do
foo() = 10;

struct S { ... };
S bar();
S s1;
bar() = s1;

bar() is an xvalue

int foo[10];
auto test = foo;

test becomes a pointer to the first element of the array

foo is a function
auto test = foo;

test becomes a function pointer

int foo[10][20][20];
auto test=foo;

test is a pointer to an array of 20 arrays of 20 ints


1 std::vector<int> x;
3 for(auto elem:x)
4 elem=5;

auto will just get int, not reference to int, so nothing will change


Foo is an array of 10 function pointers to functions that take in a ptr to a function that takes in 1 short and returns a reference to an array of 10 ints and return a pointer to pointer to double

double ** (*Foo [10]) (int (& (* ptr) (short))[10] )


When faced with the diamond problem

1 Xmen(): GameObject(11), Weapon(), Character() {}


1 Character():GameObject(5) {}


Overload resolution is done at compile time, then the dynamic choosing of the function based on virtual functions is done at run-time.


Default inheritance for struct is public, default inheritance for class is private - as per their default access modifiers.


Non-Virtual Interface is basically taking a public virtual function and making it private, and making a public non-virtual function that calls it. (Good for Garbage Collection).


struct U {

const int x;
int y;

U u{5};
int * p = &u.y;
*(p-1) = 10;




int(*(*foo[15])(int, double)) [100]; -> SPACE MAY BE MADE IN MEMORY



const member functions are different from non-const member functions because the this pointer used is different. REMEMBER, the functions are always called by the system with the this pointer used as the first argument. This is why non-const and const functions have different signatures.

This also means you can do the following:

void B::f2() const {
const_cast<B*>(this) -> c = . . .


const changes are considered conversions, not promotions and definitely not perfect fitsWRONG PROBABLY but const are not perfect fits


Template Pattern:

void MajorThing()


ThingA,B,C are all private virtual. This allows them to be modified in future, but MajorThing can't be modified cause it's a plain ol' function.


If a map is called "mapz" and it has keys "Point", "Circle" and "Triangle", mapz["Woo"] will make a new pair with "Woo" as the key and the content as the default value.


Abstract classes are classes with pure virtual functions.


1 if(pd=dynamic_cast<D*>(pb))	


Default arguments resolve in COMPILE TIME. THIS IS IMPORTANT. This means that if you use a pointer to VirtualFunction, it will take the default value from VirtualFunction

This means that the function may be the derived one, but the parameter is the compile-time default value.

1 #include <iostream>
3 class B
4 {
5 public:
6   virtual void foo(int x=3)
7   {
8     std::cout<<x<<std::endl;
9   }
10 };
12 class D: public B
13 {
14 public:
15   void foo(int x=5)
16   {
17     std::cout<<x<<std::endl;
18   }
19 };
21 int main(void)
22 {
23   B* b = new D();
24   b->foo();
25 }

In this case, 3 is outputted.


virtual destructors are recommended. If a destructor is called on, say, B * thing = new A();, then the destructor for B will be called IF the destructor is non-virtual.


Only write the virtual function in the base class. If possible, only write virtual in the base class.


vptr is at the start of a class/struct.


dynamic_cast is used for cases where you don't know what the dynamic type of the object is. You cannot use dynamic_cast if you downcast and the argument type is not polymorphic.

static_cast is used for cases where you basically want to reverse an implicit conversion, with a few restrictions and additions. static_cast performs no runtime checks. This should be used if you know that you refer to an object of a specific type, and thus a check would be unnecessary. It is any safe cast that you can do in compile time.

Unlike static_cast, but like const_cast, the reinterpret_cast expression does not compile to any CPU instructions. It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type.


Source code portability vs Binary portability

Source code portability - Compiles with GNU, or cl, or on windows or linux or mac or whatever.
Binary portability - The exact same executable can be used on different machines. For example, some exe's can be used in Windows 7 AND Windows 10.


We generally do not want impure virtual functions.


Static inheritance is stuff like templates and overloading.


An idiom is to have two Updates, Update(float dt) and _Update(float dt), the first is public, the second is protected, first calls seconds. This is used to hide certain info from the user.


1 C(const C&) = default;

default is a new C++11 feature.

It means that you want to use the compiler-generated version of that function, so you don't need to specify a body.

You can also use = delete to specify that you don't want the compiler to generate that function automatically.


If dynamic_cast fails, it returns a nullptr.

You can only dynamic_cast things that have a vptr.

dynamic_cast is done during run-time.


Member functions are like normal functions except the first argument is always the "this" pointer...


To figure out pointers, use the right-left rule.

Go right until you cant, then go left.

1 void (*FNPTR1) (int, double)


Member Functions do not add to the size of the object, since they are added to the Text area.


A small thing to note - remember to add in a semicolon after the class definition, you always forget that.


Byte addressible means that every byte in memory has its own address.


Objects are constructed in the order of declaration, not in the member initialization list.


Object creation:
Let's say we have a class X, which contains Obj1, Obj2, and Obj3 (declared in that order). When constructing X, it will be done as such:

Obj1, Obj2, Obj3, X

Also, let's say you create something without modifying the value in the initialization list. You'd create the object twice if you assigned to it in the constructor.


Static stuff:

1 class Goodness
2 {
3 	static int me;
4 };

The above class is only 1 byte - the amount alloted for an empty class.

This is because since me is static, it is a global variable.

Moreover, static variables are initialized BEFORE the main function is called.


Rule of Three must only be done when you do dynamic memory allocation DIRECTLY. For example, if you have a vector in a class, it's fine, it handles it. But if you have a pointer pointing to some dynamic memory within the class, you gotta use the Rule of Three... The three functions in question are the Destructor, Copy Constructor and Copy Assignment operators.


You don't check for nullptr for new even though you do for malloc, because new has a bunch of versions - there is new and new[] of course, but each of them have two versions each (four total) - a throw and a nonthrowable (troll) version.

The default version is the throw version. If it runs out of memory, it throws std::bad_alloc. It's better cause you don't need an if statement.


2 int nArray[5] = { 0, 1, 2, 3, 4 };
3 cout << nArray[2] << endl;            // prints "2"
4 cout << 2[nArray] << endl;            // prints "2"

In the preceding example, the expression nArray[2] is identical to 2[nArray]. The reason is that the result of a subscript expression e1[ e2 ] is given by:

*( (e2) + (e1) )


Backlinks: index:CS225 Notes