DEV Community

Isaac JL
Isaac JL

Posted on

First Post, Motivations, Some Notes

First Post

Hi there.

I've spent the morning looking for something like this and I'm glad to have found it. Never done any writing before, so this is my First Post.

Motivations

I want to document the things I'm doing on a daily basis and the things that I'm learning. So these are my Motivations.

Notes

And for the Notes, I'm just going to dive right into content that I'm learning
about right now, and review some items from Scott Meyers's Effective Modern C++. Keep in mind that as his descriptions are more informal, mine will tend to be as well, and I will most likely be very close in wording to what he has in the text. Also note that I will be reading the items in chronological order so I may come back to some posts to make edits and add notes about new things I learned later on.

So that this review is effective for me, I'm going to write as much as possible from memory and understanding, and then make it explicitly clear in the blog when I made reference to the text using the following format:

RSB Content that I did not recall and sourced entirely from the text RSE

and

RCB Content that I was unsure of and needed to use the text to confirm
RCE

All content outside of these 'reference parentheses' is entirely from memory.

Let's go.

First Review Item

How template type deduction works. There are three cases
that will allow you to deduce what T is in the templated function:

template <typename T> void f(ParamType t) {}
...
f(expr);

Each case relies on the form of ParamType:

  1. ParamType is a reference or a pointer RSB and not a universal reference RSE.
  2. ParamType is RCB a universal reference RCE.
  3. ParamType is neither a reference nor a pointer.

Case 1

In this case you follow these steps:

  1. If expr is a reference, ignore the reference part.
  2. Use the classic inspection technique (Meyers calls it pattern matching) to figure out what T should be.
Example
template <typename T> void f(T& t) {}
...
int x = 0;
int& r_x = x;
const int& rc_x = x;

f(x); // Since x is not a reference, nothing to ignore,
      // By inspection, we can determine that T must be int

f(r_x); // Since r_x is a reference, we ignore the reference part,
        // Then by inspection, we again determine that T must be int

f(rc_x); // Since rc_x is a reference, we ignore the reference part,
         // Then by inspection, we determine that T must be const int

Case 2

In this case, you follow these guidelines:

  1. If expr is an lvalue, then both ParamType and T are of type lvalue reference. Note that this is strange for them both to be lvalue references.
  2. If expr is an rvalue, then follow case 1 rules
Example
template <typename T> void f(T&& t) {}
...
int x = 0;
int& r_x = x;
const int& rc_x = x;

f(x); // x is an lvalue here, so T is int& and ParamType is also int&

f(r_x); // r_x is an lvalue, so T and ParamType are int&

f(rc_x); // rc_x is an lvalue, so T and ParamType are const int&

f(27); // 27 is an rvalue, so we follow case 1 rules to determine that T should
       // be int and so ParamType is RSB int&& RSE.

Case 3

In this case, we follow these steps:

  1. Ignore reference.
  2. RCB Ignore any cv qualifiers RCE.
Example
template <typename T> void f(T t) {}
...
int x = 0;
const int c_x = x;
const int& rc_x = x;

f(x); // T and ParamType are int

f(c_x); // Ignore the const, so T and ParamType are int

f(rc_x); // Ignore the reference, then the const, so T and ParamType are int.

Now, we're done with the cases, but Meyers continues on with some other notes
relating to arrays decaying to pointers and functions decaying to function
pointers.

Arrays decaying to pointers

RSB
Meyers notes that we often believe that pointers and arrays are
interchangeable, according to the following examples:

// Example 1
void func(int param[]);

// The above declaration is actually equivalent to this declaration using a pointer
void func(int *param);

// Example 2
const int important_numbers[] = {1, 25, 125};

const int * ptr_to_important_numbers = important_numbers; // array decays to pointer

// Example 3 (uses important_numbers declared in previous example)
template <typename T> void func(T t) {}

func(important_numbers); // T is deduced to be const int * here

However, if the form of the parameter type in a template is a reference, T will
actually be deduced to be an array of the correct type, where the type also
includes the size of the array. So, if we modified the above Example 3 to be

template <typename T> void func(T& t) {}

func(important_numbers);

T would successfully be deduced to be const int (&)[3]. This allows you to
deduce the size of arrays at compile time interestingly enough:

template <typename T, int N> constexpr std::size_t sizeOfArray(T (&)[N])
noexcept {
  return N;
}
Functions decaying to function pointers

He also notes similar things that happen to functions:

template <typename T> void f1(T t) {}

template <typename T> void f2(T& t) {}

void myFunc(const char*, int); // myFunc has type void(const char*, int)

f1(myFunc); // T is deduced to type void (*)(const char*, int)
f2(myFunc); // T is deduced to type void (&)(const char*, int)

Top comments (0)