Reference: https://github.com/changkun/modern-cpp-tutorial
- Language behaviour that occurred before the runtime.
Constants
nullptr:
- CPP does not allow implicit conversion of void * to other types.
- Traditional CPP treats null and 0 same.
- So to certain how to deal with NULL in cpp, use nullptr
- By doing so we can do the right function calls.
constexpr:
- Expression produces the same result without any side effects.
- Compiler will optimise and embed it at compile time.
- It is useful as CPP array length must be constant expression; yet most compilers these days still support as part of its optimisation.
- Therefore, use constexpr for array length so that it can be compiled.
1 | // Below is C++11 form, from 14, you can write like a function. |
Variables and Initialisation
IF Statement
In C++17, following is available like Go
1 | // Original |
Initialiser_List
- It is the one that allows to initialise the different type with same syntax: {} introduced in C++11
- Auto used in followings:
- Used with {} at Initialisation and assignment expression.
- Binded to auto including ranged for.
- It can be implemented with 2 ptr, or 1 ptr and size.
- Be aware that it is a shallow copy which does not guarantee the data for the original instance deleted.
- Possible errors in narrowing conversion like int to double.
- Which means we can use itr for array.
Structured Binding
- Provide functionality similar to the multiple return values
- Use std::tuple to return multiple values in tuple form.
- But, unfortunately, we have to clearly define how many values the returned tuple has.
1 | std::tuple<int, double, std::string> f(){ |
Type Inference
auto
- Cannot be used for function arguments.
- Cannot be used to derive array types: auto arr[10] = arrOrigin; // cannot infer array type.
decltype
- Used to solve the defect that the auto keyword can only type that variable -> use expression!
1
2
3
4auto x = 1; // int
auto y = 2.1; // double
decltype(x+y) z; // double
z = 1; // z is still double = 1.0.
tail type inference
Quick Note: In specifying a template (naming), class and typename are interchangeable = equivalent. However: special cases are
In the case of dependent types. typename is used to declare when referencing a nested type that depends on another template parameter such as typedef as following.
When you declare a template template parameter: must use class.
1
2
3
4
5
6
7
8
9// Hint at the compiler that you are referring to a dependent type
template<typename param_t>
class Foo{
typedef typename param_t::baz sub_t;
};
// declaring a template template
template <template class T> class C {}; // valid!
template <template typename T> class C{}; // Invalid!tail type inference: use decltype in specifying template.
Since the compiler will not know which type is the input parameter, decltype must be defined as a tail to define template’s return type as following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14// C++11
template<typename T, typename U>
auto add(T x, U y) -> decltype(x+y){
return x+y;
}
// C++14
template<typename T, typename U>
auto add(T x, U y){
return x+y;
}
// use link
auto w = add<int, double>(1, 2.0); // return is 3.0 as double.
decltype(auto)
- It deals with the concept of parameter forwarding in C++, will cover later.
- Mainly to derive the return type of a forwarding function or package, which does not require us to explicitly specify the parameter expression of decltype.
- 쉽게 말하면, 그냥 리턴타입 귀찮은거 알아서 해준다는 소리.
Control Flow
if constexpr in C++17
- 간단하게 if statement 에서 constexpr 사용할 수 있게되었다.
1 | template<typename T> |
Templates
Philosophy: throw all problems at compile time so that only deals with core dynamic services at runtime; optimise the performance of the run time.
Templates
- When compiler sees fully defined template, it will instantiate it in each compilation unit.
- 만약 정의만 하고 사용하지 않는다? 컴파일 되지도 않음 (디버깅 하면 함수가 존재도 안함) -> 이 이유로 다른 함수처럼 헤더에 선언만 하면 안됨. 어디든 정의도 같이.
- 만약 더하기 함수를 정의하고 int 와 double 두 가지 자료형으로 사용한다면 -> 컴파일 후 해당 템플릿의 int, double 두가지 형식의 함수가 생김.
Extern Templates
- By using extern, we can explicitly tell the compiler when to instantiate the template.
- 만약 다른 컴파일 유닛에 해당 템플릿과 동일한게 존재 한다면 (같은 자료형을 받는), 한곳에 extern 을 사용하여 해당 템플릿이 단 한번만 컴파일 되게 지정한다 (성능 향상)
1 | template class std::vector<bool>; // force instantiation |
Type alias templates & Default template Parameter
- Template is not type so cannot use typedef, but we want to make a certain type for a template like followings
- You can set default type for template parameter like a function
1 | // Template Type Alias |
Variadic Templates - 가변 길이 템플릿
- 말 그대로 가변으로써 여러 패러미터들을 따로 명시하지 않고 … 처럼 벌크로 넘길 수 있는 방식.
- bulk 로 주어진 args 를 unpack 하는 방법에는 보통 다음과 같은 형태를 따른다.
- Recursive Template Function
- Most classic approach that we can think of.
1 | template<typename TO> |
Variable parameter template expansion
Very cumbersome - can use std::bind1
2
3
4
5tempalte<typename TO, typename... T>
void printf2(TO t0, T... t){
std::cout << t0 << std::endl;
if constexpr (sizeof...(t) > 0) printf2(t...);
}Initialise list expansion
Recursive Functions are a standard practice -> drawback is that you must define a terminate function.
1 | template<typename T, typename... Ts> |
Fold expression
1 | // Available from C++17 |
- Inheritance constructor: using Parent::Parent; // Inherit parent constructor.
Explicit Virtual function overwrite
1 | struct Base { |
To Avoid above situation C++11 introduced override and final.
Override
- explicitly tells the compiler to overload -> compiler check the base func if it has a virtual function, otherwise throw error: virtual void foo() override;
Final
- To prevent the class inherit and to terminate the virtual function to be overloaded.
1
2
3
4
5
6
7
8
9struct Base{
// No one can inherit & overload this function (so no same name).
virtual void foo() final;
};
// No one can inherit this Child class.
struct Child final : Base {
// Legal, but Child cannot be parent because of final.
};
Delete and default
As discussed, compiler generates default constructor, destructor, and copy assignment if not defined.
But if you want to prohibit certain behaviour, it must be defined in private -> not comfty.
By using default and delete, you can remain it in the public scope like following:
1 | class Base { |