Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 866bbf16 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Cleaning-up template part.

parent a0962558
Branches
Tags
No related merge requests found
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) # [Getting started in C++](/) - [Templates](./0-main.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
* [Introduction to the concept of templates](/notebooks/4-Templates/1-Intro.ipynb) * [Introduction to the concept of templates](./1-Intro.ipynb)
* [TP 12](/notebooks/4-Templates/1b-TP.ipynb) * [TP 12](./1b-TP.ipynb)
* [Specialization](/notebooks/4-Templates/2-Specialization.ipynb) * [Specialization](./2-Specialization.ipynb)
* [Special syntax: typename, template and mandatory this](/notebooks/4-Templates/3-Syntax.ipynb) * [Special syntax: typename, template and mandatory this](./3-Syntax.ipynb)
* [TP 13](/notebooks/4-Templates/3b-TP.ipynb) * [TP 13](./3b-TP.ipynb)
* [Metaprogramming](/notebooks/4-Templates/4-Metaprogramming.ipynb) * [Metaprogramming](./4-Metaprogramming.ipynb)
* [Hints to more advanced concepts with templates](/notebooks/4-Templates/5-MoreAdvanced.ipynb) * [Hints to more advanced concepts with templates](./5-MoreAdvanced.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_ © _CNRS 2016_ - _Inria 2018-2020_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_ _This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_ _The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
This diff is collapsed.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [Specialization](/notebooks/4-Templates/2-Specialization.ipynb) # [Getting started in C++](/) - [Templates](./0-main.ipynb) - [Specialization](./2-Specialization.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1> <h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Total-specialization" data-toc-modified-id="Total-specialization-1">Total specialization</a></span><ul class="toc-item"><li><span><a href="#For-template-functions" data-toc-modified-id="For-template-functions-1.1">For template functions</a></span></li><li><span><a href="#For-template-classes" data-toc-modified-id="For-template-classes-1.2">For template classes</a></span></li></ul></li><li><span><a href="#Partial-specialization" data-toc-modified-id="Partial-specialization-2">Partial specialization</a></span><ul class="toc-item"><li><span><a href="#For-classes" data-toc-modified-id="For-classes-2.1">For classes</a></span></li><li><span><a href="#But-not-for-functions!" data-toc-modified-id="But-not-for-functions!-2.2">But not for functions!</a></span></li><li><span><a href="#Mimicking-the-partial-template-specialization-for-functions" data-toc-modified-id="Mimicking-the-partial-template-specialization-for-functions-2.3">Mimicking the partial template specialization for functions</a></span><ul class="toc-item"><li><span><a href="#Using-a-class-and-a-static-method" data-toc-modified-id="Using-a-class-and-a-static-method-2.3.1">Using a class and a static method</a></span></li></ul></li><li><span><a href="#If-constexpr" data-toc-modified-id="If-constexpr-2.4">If constexpr</a></span></li></ul></li></ul></div> <div class="toc"><ul class="toc-item"><li><span><a href="#Total-specialization" data-toc-modified-id="Total-specialization-1">Total specialization</a></span><ul class="toc-item"><li><span><a href="#For-template-functions" data-toc-modified-id="For-template-functions-1.1">For template functions</a></span></li><li><span><a href="#For-template-classes" data-toc-modified-id="For-template-classes-1.2">For template classes</a></span></li></ul></li><li><span><a href="#Partial-specialization" data-toc-modified-id="Partial-specialization-2">Partial specialization</a></span><ul class="toc-item"><li><span><a href="#For-classes" data-toc-modified-id="For-classes-2.1">For classes</a></span></li><li><span><a href="#But-not-for-functions!" data-toc-modified-id="But-not-for-functions!-2.2">But not for functions!</a></span></li><li><span><a href="#Mimicking-the-partial-template-specialization-for-functions" data-toc-modified-id="Mimicking-the-partial-template-specialization-for-functions-2.3">Mimicking the partial template specialization for functions</a></span><ul class="toc-item"><li><span><a href="#Using-a-class-and-a-static-method" data-toc-modified-id="Using-a-class-and-a-static-method-2.3.1">Using a class and a static method</a></span></li></ul></li><li><span><a href="#If-constexpr" data-toc-modified-id="If-constexpr-2.4">If constexpr</a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Total specialization ## Total specialization
### For template functions ### For template functions
Let's take back our previous `Convert` example: the conversion into a `std::string` wasn't valid with the proposed instantiation, but converting an integer into a `std::string` is not an outlandish idea either... Let's take back our previous `Convert` example: the conversion into a `std::string` wasn't valid with the proposed instantiation, but converting an integer into a `std::string` is not an outlandish idea either...
There is a mechanism to provide specific instantiation for a type: the **(total) specialization** (we'll see why _total_ shortly): There is a mechanism to provide specific instantiation for a type: the **(total) specialization** (we'll see why _total_ shortly):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class T> template<class T>
T Convert(int value) T Convert(int value)
{ {
std::cout << "Generic instantiation called!" << std::endl; std::cout << "Generic instantiation called!" << std::endl;
return static_cast<T>(value); return static_cast<T>(value);
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <string> #include <string>
template<> // notice the empty brackets! template<> // notice the empty brackets!
std::string Convert<std::string>(int value) std::string Convert<std::string>(int value)
{ {
std::cout << "std::string instantiation called!" << std::endl; std::cout << "std::string instantiation called!" << std::endl;
return std::to_string(value); return std::to_string(value);
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
Convert<double>(5); Convert<double>(5);
Convert<std::string>(5); Convert<std::string>(5);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Please notice the syntax: the definition provides template with no parameter in the brackets, and the `T` is replaced by the specialized type. As there are no template parameters left, the definition of the specialization must be put in a compiled file (or with an [`inline` keyword](/notebooks/1-ProceduralProgramming/4-Functions.ipynb#inline-functions) in header file) Please notice the syntax: the definition provides template with no parameter in the brackets, and the `T` is replaced by the specialized type. As there are no template parameters left, the definition of the specialization must be put in a compiled file (or with an [`inline` keyword](../1-ProceduralProgramming/4-Functions.ipynb#inline-functions) in header file)
Of course, as many specialization as you wish may be provided: Of course, as many specialization as you wish may be provided:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
class HoldAnInt class HoldAnInt
{ {
public: public:
explicit HoldAnInt(int value); explicit HoldAnInt(int value);
private: private:
int value_; int value_;
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
HoldAnInt::HoldAnInt(int value) HoldAnInt::HoldAnInt(int value)
: value_(value) : value_(value)
{ } { }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<> template<>
HoldAnInt Convert(int value) HoldAnInt Convert(int value)
{ {
std::cout << "HoldAnInt instantiation called!" << std::endl; std::cout << "HoldAnInt instantiation called!" << std::endl;
return HoldAnInt(value); return HoldAnInt(value);
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
Convert<double>(5); Convert<double>(5);
Convert<std::string>(5); Convert<std::string>(5);
Convert<HoldAnInt>(5); Convert<HoldAnInt>(5);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### For template classes ### For template classes
Total specialization was illustrated on a function, but it works as well for a class. You may choose to: Total specialization was illustrated on a function, but it works as well for a class. You may choose to:
* Specialize either the entire class (in which case you have to define the complete API for the specialized class) * Specialize either the entire class (in which case you have to define the complete API for the specialized class)
* Or just a method. * Or just a method.
The case below provides entire class specialization for `double` and just method specialization for `std::string`: The case below provides entire class specialization for `double` and just method specialization for `std::string`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
// First the generic class `HoldAValue` // First the generic class `HoldAValue`
#include <iostream> #include <iostream>
#include <string> #include <string>
template<class T> template<class T>
class HoldAValue class HoldAValue
{ {
public: public:
HoldAValue(T value); HoldAValue(T value);
T GetValue() const; T GetValue() const;
private: private:
T value_; T value_;
}; };
template<class T> template<class T>
HoldAValue<T>::HoldAValue(T value) HoldAValue<T>::HoldAValue(T value)
: value_(value) : value_(value)
{ } { }
template<class T> template<class T>
T HoldAValue<T>::GetValue() const T HoldAValue<T>::GetValue() const
{ {
return value_; return value_;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
// Method specialization for std::string // Method specialization for std::string
template<> template<>
std::string HoldAValue<std::string>::GetValue() const std::string HoldAValue<std::string>::GetValue() const
{ {
return "String case (through method specialization): " + value_; return "String case (through method specialization): " + value_;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
// Class specialization for double // Class specialization for double
template<> template<>
class HoldAValue<double> class HoldAValue<double>
{ {
public: public:
HoldAValue(std::string blah, double value); HoldAValue(std::string blah, double value);
double GetValue() const; double GetValue() const;
private: private:
std::string blah_; std::string blah_;
double value_; double value_;
}; };
HoldAValue<double>::HoldAValue(std::string blah, double value) HoldAValue<double>::HoldAValue(std::string blah, double value)
: blah_(blah), : blah_(blah),
value_(value) value_(value)
{ } { }
double HoldAValue<double>::GetValue() const double HoldAValue<double>::GetValue() const
{ {
std::cout << blah_; std::cout << blah_;
return value_; return value_;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
HoldAValue<int> integer(5); HoldAValue<int> integer(5);
std::cout << integer.GetValue() << std::endl; std::cout << integer.GetValue() << std::endl;
HoldAValue<std::string> string("Hello world"); HoldAValue<std::string> string("Hello world");
std::cout << string.GetValue() << std::endl; std::cout << string.GetValue() << std::endl;
HoldAValue<double> dbl("Double case (through class specialization): ", 3.14); HoldAValue<double> dbl("Double case (through class specialization): ", 3.14);
std::cout << dbl.GetValue() << std::endl; std::cout << dbl.GetValue() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Partial specialization ## Partial specialization
### For classes ### For classes
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
It is also possible for classes to **specialize partially** a template class: It is also possible for classes to **specialize partially** a template class:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class TypeT, std::size_t Nelts> template<class TypeT, std::size_t Nelts>
class MyArray class MyArray
{ {
public: public:
explicit MyArray(TypeT initial_value); explicit MyArray(TypeT initial_value);
private: private:
TypeT content_[Nelts]; TypeT content_[Nelts];
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class TypeT, std::size_t Nelts> template<class TypeT, std::size_t Nelts>
MyArray<TypeT, Nelts>::MyArray(TypeT initial_value) MyArray<TypeT, Nelts>::MyArray(TypeT initial_value)
{ {
for (auto i = 0ul; i < Nelts; ++i) for (auto i = 0ul; i < Nelts; ++i)
content_[i] = initial_value; content_[i] = initial_value;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class TypeT> template<class TypeT>
class MyArray<TypeT, 10ul> class MyArray<TypeT, 10ul>
{ {
public: public:
explicit MyArray(TypeT initial_value); explicit MyArray(TypeT initial_value);
private: private:
TypeT content_[10ul]; TypeT content_[10ul];
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class TypeT> template<class TypeT>
MyArray<TypeT, 10ul>::MyArray(TypeT initial_value) MyArray<TypeT, 10ul>::MyArray(TypeT initial_value)
{ {
std::cout << "Partial specialization constructor: type is still templated but size is fixed " << std::endl; std::cout << "Partial specialization constructor: type is still templated but size is fixed " << std::endl;
for (auto i = 0ul; i < 10ul; ++i) for (auto i = 0ul; i < 10ul; ++i)
content_[i] = initial_value; content_[i] = initial_value;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
MyArray<int, 10ul> array1(2); MyArray<int, 10ul> array1(2);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### But not for functions! ### But not for functions!
However, partial specialization is **forbidden** for template functions: However, partial specialization is **forbidden** for template functions:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class T, class U> template<class T, class U>
void PrintSquare(T t, U u) void PrintSquare(T t, U u)
{ {
std::cout << "Generic instantiation" << std::endl; std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
PrintSquare(5, 7.); PrintSquare(5, 7.);
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <string> #include <string>
template<class T> template<class T>
void PrintSquare<T, int>(T t, std::string u) void PrintSquare<T, int>(T t, std::string u)
{ {
std::cout << "Partial function specialization: doesn't compile!" << std::endl; std::cout << "Partial function specialization: doesn't compile!" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
You might have the impress it works if you try defining the function without specifying the brackets: You might have the impress it works if you try defining the function without specifying the brackets:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <string> #include <string>
template<class T> template<class T>
void PrintSquare(T t, std::string u) void PrintSquare(T t, std::string u)
{ {
std::cout << "Seamingly ok function template specialization " << std::endl; std::cout << "Seamingly ok function template specialization " << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
// May fail to Xeus-cling; but expected behaviour is to 'work' but not as intended... // May fail to Xeus-cling; but expected behaviour is to 'work' but not as intended...
{ {
std::string hello("Hello"); std::string hello("Hello");
PrintSquare(5., hello); PrintSquare(5., hello);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
but you actually did simply an overload of `Print()`; calling the explicit specialization fails: but you actually did simply an overload of `Print()`; calling the explicit specialization fails:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
PrintSquare<double, std::string>(5., std::string("Hello")); PrintSquare<double, std::string>(5., std::string("Hello"));
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To be honest, the reason partial specialization are forbidden are quite unclear for me; this seems to be internal reasons in the way compilers are parsing the C++ code. To be honest, the reason partial specialization are forbidden are quite unclear for me; this seems to be internal reasons in the way compilers are parsing the C++ code.
### Mimicking the partial template specialization for functions ### Mimicking the partial template specialization for functions
#### Using a class and a static method #### Using a class and a static method
There is a quite verbosy way to circumvent the impossibility: use a template struct (which is partially specializable) with a static method... There is a quite verbosy way to circumvent the impossibility: use a template struct (which is partially specializable) with a static method...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class T, class U> template<class T, class U>
struct PrintSquareHelper struct PrintSquareHelper
{ {
static void Do(T t, U u) static void Do(T t, U u)
{ {
std::cout << "Generic instantiation" << std::endl; std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <string> #include <string>
template<class T> template<class T>
struct PrintSquareHelper<T, std::string> struct PrintSquareHelper<T, std::string>
{ {
static void Do(T t, std::string u) static void Do(T t, std::string u)
{ {
std::cout << "Specialized instantiation" << std::endl; std::cout << "Specialized instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T, class U> template<class T, class U>
void PrintSquare2(T t, U u) void PrintSquare2(T t, U u)
{ {
PrintSquareHelper<T, U>::Do(t, u); PrintSquareHelper<T, U>::Do(t, u);
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
PrintSquare2<double, std::string>(5., std::string("Hello")); PrintSquare2<double, std::string>(5., std::string("Hello"));
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### If constexpr ### If constexpr
C++ 17 introduced (at last!) a special `if` that is explicitly checked at compile time. C++ 17 introduced (at last!) a special `if` that is explicitly checked at compile time.
This alleviates greatly the codes and is really something I yearned for years (but it requires you to use quite a recent compiler): This alleviates greatly the codes and is really something I yearned for years (but it requires you to use quite a recent compiler):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
#include <string> #include <string>
template<class T, class U> template<class T, class U>
void Print3(T t, U u) void Print3(T t, U u)
{ {
if constexpr (std::is_same<U, std::string>()) if constexpr (std::is_same<U, std::string>())
{ {
std::cout << "Specialized instantiation" << std::endl; std::cout << "Specialized instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
} }
else else
{ {
std::cout << "Generic instantiation" << std::endl; std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
} }
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
Print3<double, std::string>(5., "hello"); Print3<double, std::string>(5., "hello");
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
**Note: and without constexpr?** **Note: and without constexpr?**
I haven't specified it so far, but the `constexpr` is crucial: without it the compiler must instantiate both branches, even if only one is kept at the end. And if one can't be instantiated we're screwed: I haven't specified it so far, but the `constexpr` is crucial: without it the compiler must instantiate both branches, even if only one is kept at the end. And if one can't be instantiated we're screwed:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class T, class U> template<class T, class U>
void Print4(T t, U u) void Print4(T t, U u)
{ {
if (std::is_same<U, std::string>()) if (std::is_same<U, std::string>())
{ {
std::cout << "Specialized instantiation" << std::endl; std::cout << "Specialized instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
} }
else else
{ {
std::cout << "Generic instantiation" << std::endl; std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl; std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
} }
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
Print4<double, std::string>(5., "Hello"); // Compilation error! Print4<double, std::string>(5., "Hello"); // Compilation error!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_ © _CNRS 2016_ - _Inria 2018-2020_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_ _This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_ _The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [Special syntax: typename, template and mandatory this](/notebooks/4-Templates/3-Syntax.ipynb) # [Getting started in C++](/) - [Templates](./0-main.ipynb) - [Special syntax: typename, template and mandatory this](./3-Syntax.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1> <h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Mandatory-this-in-calling-base-class-methods" data-toc-modified-id="Mandatory-this-in-calling-base-class-methods-1">Mandatory <code>this</code> in calling base class methods</a></span></li><li><span><a href="#Mandatory-typename-keyword" data-toc-modified-id="Mandatory-typename-keyword-2">Mandatory <code>typename</code> keyword</a></span></li><li><span><a href="#Mandatory-template-keyword" data-toc-modified-id="Mandatory-template-keyword-3">Mandatory <code>template</code> keyword</a></span></li><li><span><a href="#Good-practice:-always-check-obvious-template-instantiations-in-test!" data-toc-modified-id="Good-practice:-always-check-obvious-template-instantiations-in-test!-4">Good practice: always check obvious template instantiations in test!</a></span></li><li><span><a href="#No-virtual-template-method!" data-toc-modified-id="No-virtual-template-method!-5">No virtual template method!</a></span></li></ul></div> <div class="toc"><ul class="toc-item"><li><span><a href="#Mandatory-this-in-calling-base-class-methods" data-toc-modified-id="Mandatory-this-in-calling-base-class-methods-1">Mandatory <code>this</code> in calling base class methods</a></span></li><li><span><a href="#Mandatory-typename-keyword" data-toc-modified-id="Mandatory-typename-keyword-2">Mandatory <code>typename</code> keyword</a></span></li><li><span><a href="#Mandatory-template-keyword" data-toc-modified-id="Mandatory-template-keyword-3">Mandatory <code>template</code> keyword</a></span></li><li><span><a href="#Good-practice:-always-check-obvious-template-instantiations-in-test!" data-toc-modified-id="Good-practice:-always-check-obvious-template-instantiations-in-test!-4">Good practice: always check obvious template instantiations in test!</a></span></li><li><span><a href="#No-virtual-template-method!" data-toc-modified-id="No-virtual-template-method!-5">No virtual template method!</a></span></li></ul></div>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In this chapter, we will review some cases involving templates in which additional syntax are required to help the compiler do its job; it is useful to have a look because these not that difficult rules might be puzzling if their whereabouts have not been explained a bit. In this chapter, we will review some cases involving templates in which additional syntax are required to help the compiler do its job; it is useful to have a look because these not that difficult rules might be puzzling if their whereabouts have not been explained a bit.
## Mandatory `this` in calling base class methods ## Mandatory `this` in calling base class methods
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T> template<class T>
class Base class Base
{ {
public: public:
Base() = default; Base() = default;
void BaseMethod() void BaseMethod()
{ } { }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T> template<class T>
class Derived : public Base<T> class Derived : public Base<T>
{ {
public: public:
Derived() = default; Derived() = default;
void DerivedMethod(); void DerivedMethod();
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T> template<class T>
void Derived<T>::DerivedMethod() void Derived<T>::DerivedMethod()
{ {
BaseMethod(); // Compilation error! BaseMethod(); // Compilation error!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Same code without the template argument would be accepted without issue... but with templates the compiler doesn't manage to properly find the base method. Same code without the template argument would be accepted without issue... but with templates the compiler doesn't manage to properly find the base method.
To circumvent this, you must explicitly tell `BaseMethod()` is a method; the most obvious way is to put explicit `this`: To circumvent this, you must explicitly tell `BaseMethod()` is a method; the most obvious way is to put explicit `this`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T> template<class T>
void Derived<T>::DerivedMethod() void Derived<T>::DerivedMethod()
{ {
this->BaseMethod(); // Now ok! this->BaseMethod(); // Now ok!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Personally, I even prefer to use an alias which points to the base class: it is even clearer this way where is defined the method (even more so if there are several parent classes). Personally, I even prefer to use an alias which points to the base class: it is even clearer this way where is defined the method (even more so if there are several parent classes).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T> template<class T>
class Derived2 : public Base<T> class Derived2 : public Base<T>
{ {
public: public:
Derived2() = default; Derived2() = default;
using parent = Base<T>; using parent = Base<T>;
void DerivedMethod() void DerivedMethod()
{ {
parent::BaseMethod(); // also ok! parent::BaseMethod(); // also ok!
} }
}; };
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Mandatory `typename` keyword ## Mandatory `typename` keyword
In a `std::vector`, as in several STL constructs, there are several **traits** which are defined: a trait is a type information concerning the class. [This page](https://en.cppreference.com/w/cpp/container/vector) mentions all of them. In a `std::vector`, as in several STL constructs, there are several **traits** which are defined: a trait is a type information concerning the class. [This page](https://en.cppreference.com/w/cpp/container/vector) mentions all of them.
The first one simply returns the type of the value stored in a `std::vector`, for instance for `std::vector<int>` `value_type` is just `int`. The first one simply returns the type of the value stored in a `std::vector`, for instance for `std::vector<int>` `value_type` is just `int`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <vector> #include <vector>
{ {
std::vector<int>::value_type i = 5; // convoluted way to just say int i = 5! std::vector<int>::value_type i = 5; // convoluted way to just say int i = 5!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
However, this simple construct fails in the following case: However, this simple construct fails in the following case:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class ContainerT> template<class ContainerT>
void PrintFive() void PrintFive()
{ {
std::cout << static_cast<ContainerT::value_type>(5) << std::endl; std::cout << static_cast<ContainerT::value_type>(5) << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
You might see it's just that it was never mentioned `ContainerT` was intended to be a `std::vector`... and you would be right. That's not the full story though: the point of a template function is to just work with any given class that just respects the interface (it's very similar to the [**duck typing**](https://en.wikipedia.org/wiki/Duck_typing) so dear to Pythonists: _If it walks like a duck and it quacks like a duck, then it must be a duck_). You might see it's just that it was never mentioned `ContainerT` was intended to be a `std::vector`... and you would be right. That's not the full story though: the point of a template function is to just work with any given class that just respects the interface (it's very similar to the [**duck typing**](https://en.wikipedia.org/wiki/Duck_typing) so dear to Pythonists: _If it walks like a duck and it quacks like a duck, then it must be a duck_).
The issue in fact here is that the compiler doesn't even know that `value_type` is expected to be a type and not an attribute. So you have to help him with the keyword `typename` just before the type in question (just as indicated by the compiler: fortunately they are now rather helpful in pointing out this kind of issue): The issue in fact here is that the compiler doesn't even know that `value_type` is expected to be a type and not an attribute. So you have to help him with the keyword `typename` just before the type in question (just as indicated by the compiler: fortunately they are now rather helpful in pointing out this kind of issue):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class ContainerT> template<class ContainerT>
void PrintFive() void PrintFive()
{ {
std::cout << static_cast<typename ContainerT::value_type>(5) << std::endl; std::cout << static_cast<typename ContainerT::value_type>(5) << std::endl;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
PrintFive<int>(); // Won't compile: no trait 'value_type' for an 'int'! PrintFive<int>(); // Won't compile: no trait 'value_type' for an 'int'!
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
PrintFive<std::vector<int>>(); // Ok! PrintFive<std::vector<int>>(); // Ok!
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Mandatory `template` keyword ## Mandatory `template` keyword
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Likewise, compiler sometimes needs help to figure out a template is at bay. For instance: Likewise, compiler sometimes needs help to figure out a template is at bay. For instance:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
struct Foo struct Foo
{ {
template<int N> template<int N>
void PrintN() const; void PrintN() const;
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<int N> template<int N>
void Foo::PrintN() const void Foo::PrintN() const
{ {
std::cout << N << std::endl; std::cout << N << std::endl;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class U> template<class U>
void Print(const U& u) void Print(const U& u)
{ {
std::cout << u.PrintN<0>() << std::endl; std::cout << u.PrintN<0>() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Here the compiler is confused and doesn't understand we mean `PrintN` to be a template method; there is in fact an ambiguity with operators `<` and `>`. These ambiguity cases arises when two templates are involved. Here the compiler is confused and doesn't understand we mean `PrintN` to be a template method; there is in fact an ambiguity with operators `<` and `>`. These ambiguity cases arises when two templates are involved.
To help the compiler figure it out, the solution is very similar to the `typename` one: we specify an explicit template to help the compiler (unfortunately the compiler is way less helpful than in the `typename` case here...): To help the compiler figure it out, the solution is very similar to the `typename` one: we specify an explicit template to help the compiler (unfortunately the compiler is way less helpful than in the `typename` case here...):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class U> template<class U>
void Print(const U& u) void Print(const U& u)
{ {
std::cout << u.template PrintN<0>() << std::endl; // Notice the template keyword! std::cout << u.template PrintN<0>() << std::endl; // Notice the template keyword!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Good practice: always check obvious template instantiations in test! ## Good practice: always check obvious template instantiations in test!
If you have been attentive, you might see `Print()` above is faulty: we ask to print on screen a void function... An instantiation reveals the issue: If you have been very attentive, you might see `Print()` above is faulty: we ask to print on screen a void function... An instantiation reveals the issue:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
Foo foo; Foo foo;
Print(foo); // Compilation error! Print(foo); // Compilation error!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Without instantiation, the compiler can't easily figure out there is an issue; in `Print()` there are actually implicit conditions on the nature if the template parameter `U`: Without instantiation, the compiler can't easily figure out there is an issue; in `Print()` there are actually implicit conditions on the nature if the template parameter `U`:
* `U` must provide a template `PrintN` method with an integer parameter. * `U` must provide a template `PrintN` method with an integer parameter.
* An overload of operator `<<` must be provided for the return type of this method. * An overload of operator `<<` must be provided for the return type of this method.
`Foo` doesn't check the second condition... but you can't really see it without checking it. `Foo` doesn't check the second condition... but you can't really see it without checking it.
So if the template is intended to work with a `Foo`, it is a good idea to provide a dedicated test that checks it actually works. So if the template is intended to work with a `Foo`, it is a good idea to provide a dedicated test that checks it actually works.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## No virtual template method! ## No virtual template method!
All is in the title here: a method can't be both virtual and template... All is in the title here: a method can't be both virtual and template...
We are speaking specifically of the methods here: a template class may feature virtual (non template) methods without any issue. We are speaking specifically of the methods here: a template class may feature virtual (non template) methods without any issue.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_ © _CNRS 2016_ - _Inria 2018-2020_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_ _This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_ _The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [Metaprogramming](/notebooks/4-Templates/4-Metaprogramming.ipynb) # [Getting started in C++](/) - [Templates](./0-main.ipynb) - [Metaprogramming](./4-Metaprogramming.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1> <h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Introduction" data-toc-modified-id="Introduction-1">Introduction</a></span></li><li><span><a href="#Example:-same-action-upon-a-collection-of-heterogenous-objets" data-toc-modified-id="Example:-same-action-upon-a-collection-of-heterogenous-objets-2">Example: same action upon a collection of heterogenous objets</a></span></li></ul></div> <div class="toc"><ul class="toc-item"><li><span><a href="#Introduction" data-toc-modified-id="Introduction-1">Introduction</a></span></li><li><span><a href="#Example:-same-action-upon-a-collection-of-heterogenous-objets" data-toc-modified-id="Example:-same-action-upon-a-collection-of-heterogenous-objets-2">Example: same action upon a collection of heterogenous objets</a></span></li></ul></div>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Introduction ## Introduction
We will no go very far in this direction: metaprogramming is really a very rich subset of C++ of its own, and one that is especially tricky to code. We will no go very far in this direction: metaprogramming is really a very rich subset of C++ of its own, and one that is especially tricky to code.
You can be a very skilled C++ developer and never use this; however it opens some really interesting prospects that can't be achieved easily (or at all...) without it. You can be a very skilled C++ developer and never use this; however it opens some really interesting prospects that can't be achieved easily (or at all...) without it.
I recommand the reading of \cite{Alexandrescu2001} to get the gist of it: even if it relies upon older versions of C++ (and therefore some of its constructs are now in one form or another in modern C++ or STL) it is very insightful to understand the reasoning behind metaprogrammation. I recommand the reading of \cite{Alexandrescu2001} to get the gist of it: even if it relies upon older versions of C++ (and therefore some of its constructs are now in one form or another in modern C++ or STL) it is very insightful to understand the reasoning behind metaprogrammation.
## Example: same action upon a collection of heterogenous objets ## Example: same action upon a collection of heterogenous objets
Let's say we want to put together in a same container multiple objects of heterogeneous type (one concrete case for which I have used that: reading an input data file with each entry handled differently by a dedicated object). Let's say we want to put together in a same container multiple objects of heterogeneous type (one concrete case for which I have used that: reading an input data file with each entry handled differently by a dedicated object).
In C++11, `std::tuple` was introduced for that purpose: In C++11, `std::tuple` was introduced for that purpose:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <tuple> #include <tuple>
std::tuple<int, std::string, double, float, long> tuple = std::tuple<int, std::string, double, float, long> tuple =
std::make_tuple(5, "hello", 5., 3.2f, -35l); std::make_tuple(5, "hello", 5., 3.2f, -35l);
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
What if we want to apply the same treatment of all of the entries? Let's roll with just printing each of them: What if we want to apply the same treatment of all of the entries? Let's roll with just printing each of them:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
std::cout << std::get<0>(tuple) << std::endl; std::cout << std::get<0>(tuple) << std::endl;
std::cout << std::get<1>(tuple) << std::endl; std::cout << std::get<1>(tuple) << std::endl;
std::cout << std::get<2>(tuple) << std::endl; std::cout << std::get<2>(tuple) << std::endl;
std::cout << std::get<3>(tuple) << std::endl; std::cout << std::get<3>(tuple) << std::endl;
std::cout << std::get<4>(tuple) << std::endl; std::cout << std::get<4>(tuple) << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The treatment was rather simple here, but the code was duplicated manually for each of them. **Metaprogramming** is the art of making the compiler generate by itself the whole code. The treatment was rather simple here, but the code was duplicated manually for each of them. **Metaprogramming** is the art of making the compiler generate by itself the whole code.
The syntax relies heavily on templates, and those of you familiar with functional programming will feel at ease here: The syntax relies heavily on templates, and those of you familiar with functional programming will feel at ease here:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template <std::size_t IndexT, std::size_t TupleSizeT> template <std::size_t IndexT, std::size_t TupleSizeT>
struct PrintTuple struct PrintTuple
{ {
template<class TupleT> // I'm lazy I won't put there std::tuple<int, std::string, double, float, long> again... template<class TupleT> // I'm lazy I won't put there std::tuple<int, std::string, double, float, long> again...
static void Do(const TupleT& tuple) static void Do(const TupleT& tuple)
{ {
std::cout << std::get<IndexT>(tuple) << std::endl; std::cout << std::get<IndexT>(tuple) << std::endl;
PrintTuple<IndexT + 1ul, TupleSizeT>::Do(tuple); // that's the catch: call recursively the next one! PrintTuple<IndexT + 1ul, TupleSizeT>::Do(tuple); // that's the catch: call recursively the next one!
} }
}; };
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
You may see the gist of it, but there is still an issue: the recursivity goes to the infinity... (don't worry your compiler will yell before that!). So you need a specialization to stop it - you may see now why I used a class template and not a function! You may see the gist of it, but there is still an issue: the recursivity goes to the infinity... (don't worry your compiler will yell before that!). So you need a specialization to stop it - you may see now why I used a class template and not a function!
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template <std::size_t TupleSizeT> template <std::size_t TupleSizeT>
struct PrintTuple<TupleSizeT, TupleSizeT> struct PrintTuple<TupleSizeT, TupleSizeT>
{ {
template<class TupleT> template<class TupleT>
static void Do(const TupleT& tuple) static void Do(const TupleT& tuple)
{ {
// Do nothing! // Do nothing!
} }
}; };
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
With that, the code is properly generated: With that, the code is properly generated:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
std::tuple<int, std::string, double, float, long> tuple = std::tuple<int, std::string, double, float, long> tuple =
std::make_tuple(5, "hello", 5., 3.2f, -35l); std::make_tuple(5, "hello", 5., 3.2f, -35l);
PrintTuple<0, std::tuple_size<decltype(tuple)>::value>::Do(tuple); PrintTuple<0, std::tuple_size<decltype(tuple)>::value>::Do(tuple);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Of course, the call is not yet very easy: it's cumbersome to have to explicitly reach the tuple size... But as often in C++ an extra level of indirection may lift this issue: Of course, the call is not yet very easy: it's cumbersome to have to explicitly reach the tuple size... But as often in C++ an extra level of indirection may lift this issue:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class TupleT> template<class TupleT>
void PrintTupleWrapper(const TupleT& t) void PrintTupleWrapper(const TupleT& t)
{ {
PrintTuple<0, std::tuple_size<TupleT>::value>::Do(t); PrintTuple<0, std::tuple_size<TupleT>::value>::Do(t);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
And then the call may be simply: And then the call may be simply:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
std::tuple<int, std::string, double, float, long> tuple = std::make_tuple(5, "hello", 5., 3.2f, -35l); std::tuple<int, std::string, double, float, long> tuple = std::make_tuple(5, "hello", 5., 3.2f, -35l);
PrintTupleWrapper(tuple); PrintTupleWrapper(tuple);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In fact, my laziness earlier when I used a template argument rather than the exact tuple type pays now as this function may be used with any tuple (or more precisely with any tuple for which all elements comply with `operator<<`): In fact, my laziness earlier when I used a template argument rather than the exact tuple type pays now as this function may be used with any tuple (or more precisely with any tuple for which all elements comply with `operator<<`):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
int a = 5; int a = 5;
std::tuple<std::string, int*> tuple = std::make_tuple("Hello", &a); std::tuple<std::string, int*> tuple = std::make_tuple("Hello", &a);
PrintTupleWrapper(tuple); PrintTupleWrapper(tuple);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# References # References
[<a id="cit-Alexandrescu2001" href="#call-Alexandrescu2001">Alexandrescu2001</a>] Andrei Alexandrescu, ``_Modern C++ Design: Generic Programming and Design Patterns applied_'', 01 2001. [<a id="cit-Alexandrescu2001" href="#call-Alexandrescu2001">Alexandrescu2001</a>] Andrei Alexandrescu, ``_Modern C++ Design: Generic Programming and Design Patterns applied_'', 01 2001.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_ © _CNRS 2016_ - _Inria 2018-2020_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_ _This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_ _The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [Hints to more advanced concepts with templates](/notebooks/4-Templates/5-MoreAdvanced.ipynb) # [Getting started in C++](/) - [Templates](./0-main.ipynb) - [Hints to more advanced concepts with templates](./5-MoreAdvanced.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1> <h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Curiously-recurrent-template-pattern-(CRTP)" data-toc-modified-id="Curiously-recurrent-template-pattern-(CRTP)-1">Curiously recurrent template pattern (CRTP)</a></span></li><li><span><a href="#Traits" data-toc-modified-id="Traits-2">Traits</a></span></li><li><span><a href="#Policies" data-toc-modified-id="Policies-3">Policies</a></span></li><li><span><a href="#Variadic-templates" data-toc-modified-id="Variadic-templates-4">Variadic templates</a></span></li><li><span><a href="#Template-template-parameters-(not-a-mistake...)" data-toc-modified-id="Template-template-parameters-(not-a-mistake...)-5">Template template parameters (not a mistake...)</a></span></li></ul></div> <div class="toc"><ul class="toc-item"><li><span><a href="#Curiously-recurrent-template-pattern-(CRTP)" data-toc-modified-id="Curiously-recurrent-template-pattern-(CRTP)-1">Curiously recurrent template pattern (CRTP)</a></span></li><li><span><a href="#Traits" data-toc-modified-id="Traits-2">Traits</a></span></li><li><span><a href="#Policies" data-toc-modified-id="Policies-3">Policies</a></span></li><li><span><a href="#Variadic-templates" data-toc-modified-id="Variadic-templates-4">Variadic templates</a></span></li><li><span><a href="#Template-template-parameters-(not-a-mistake...)" data-toc-modified-id="Template-template-parameters-(not-a-mistake...)-5">Template template parameters (not a mistake...)</a></span></li></ul></div>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We have barely scratched the surface of what can be done with templates; I will here just drop few names and a very brief explanation to allow you to dig deeper if it might seem of interest for your codes (a Google search for either of them will give you plenty of references) and also avoid you frowing upon a seemingly daunting syntax... We have barely scratched the surface of what can be done with templates; I will here just drop few names and a very brief explanation to allow you to dig deeper if it might seem of interest for your codes (a Google search for either of them will give you plenty of references) and also avoid you frowing upon a seemingly daunting syntax...
## Curiously recurrent template pattern (CRTP) ## Curiously recurrent template pattern (CRTP)
One of my own favourite idiom (so much I didn't resist writing an [entry](/notebooks/7-Appendix/Crtp.ipynb) about it in the appendix). One of my own favourite idiom (so much I didn't resist writing an [entry](../7-Appendix/Crtp.ipynb) about it in the appendix).
The idea behind it is to provide a same set of a given functionality to classes that have otherwise nothing in common. The idea behind it is to provide a same set of a given functionality to classes that have otherwise nothing in common.
The basic example is if you want to assign a unique identifier to a class of yours: the implementation would be exactly the same in each otherwise different class in which you need this: The basic example is if you want to assign a unique identifier to a class of yours: the implementation would be exactly the same in each otherwise different class in which you need this:
* Initializing properly this identifier at construction. * Initializing properly this identifier at construction.
* Check no other objects of the same class use it already. * Check no other objects of the same class use it already.
* Provide an accessor `GetUniqueIdentifier()`. * Provide an accessor `GetUniqueIdentifier()`.
Usual inheritance or composition aren't very appropriate to put in common once and for all (DRY principle!) these functionalities: either they may prove dangerous (inheritance) or be very wordy (composition). Usual inheritance or composition aren't very appropriate to put in common once and for all (DRY principle!) these functionalities: either they may prove dangerous (inheritance) or be very wordy (composition).
The **curiously recurrent template pattern** is a very specific inheritance: The **curiously recurrent template pattern** is a very specific inheritance:
````class MyClass : public UniqueIdentifier<MyClass>```` ````class MyClass : public UniqueIdentifier<MyClass>````
where your class inherits from a template class which parameter is... your class itself. where your class inherits from a template class which parameter is... your class itself.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Traits ## Traits
A **trait** is a member of a class which gives exclusively an information about type. For instance let's go back to the `HoldAValue` class we wrote [earlier](/notebooks/4-Templates/2-Specialization.ipynb) in our template presentation: A **trait** is a member of a class which gives exclusively an information about type. For instance let's go back to the `HoldAValue` class we wrote [earlier](./2-Specialization.ipynb) in our template presentation:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
#include <string> #include <string>
template<class T> template<class T>
class HoldAValue class HoldAValue
{ {
public: public:
HoldAValue(T value); HoldAValue(T value);
T GetValue() const; T GetValue() const;
private: private:
T value_; T value_;
}; };
template<class T> template<class T>
HoldAValue<T>::HoldAValue(T value) HoldAValue<T>::HoldAValue(T value)
: value_(value) : value_(value)
{ } { }
template<class T> template<class T>
T HoldAValue<T>::GetValue() const T HoldAValue<T>::GetValue() const
{ {
return value_; return value_;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
HoldAValue<int> hint(5); HoldAValue<int> hint(5);
std::cout << hint.GetValue() << std::endl; std::cout << hint.GetValue() << std::endl;
HoldAValue<std::string> sint("Hello world!"); HoldAValue<std::string> sint("Hello world!");
std::cout << sint.GetValue() << std::endl; std::cout << sint.GetValue() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This class was not especially efficient: the accessor `GetValue()`: This class was not especially efficient: the accessor `GetValue()`:
- Requires `T` is copyable. - Requires `T` is copyable.
- Copy `T`, which is potentially a time-consuming operator. - Copy `T`, which is potentially a time-consuming operator.
We could replace by `const T& GetValue() const`, but it's a bit on the nose (and less efficient) for plain old data type. The best of both world may be achieved by a trait: We could replace by `const T& GetValue() const`, but it's a bit on the nose (and less efficient) for plain old data type. The best of both world may be achieved by a trait:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
#include <string> #include <string>
template<class T> template<class T>
class ImprovedHoldAValue class ImprovedHoldAValue
{ {
public: public:
// Traits: information about type! // Traits: information about type!
using return_value = using return_value =
typename std::conditional<std::is_trivial<T>::value, T, const T&>::type; typename std::conditional<std::is_trivial<T>::value, T, const T&>::type;
ImprovedHoldAValue(T value); ImprovedHoldAValue(T value);
return_value GetValue() const; return_value GetValue() const;
private: private:
T value_; T value_;
}; };
template<class T> template<class T>
ImprovedHoldAValue<T>::ImprovedHoldAValue(T value) ImprovedHoldAValue<T>::ImprovedHoldAValue(T value)
: value_(value) : value_(value)
{ } { }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Beware the trait that acts as the return value must be scoped correctly in the definition: Beware the trait that acts as the return value must be scoped correctly in the definition:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class T> template<class T>
typename ImprovedHoldAValue<T>::return_value ImprovedHoldAValue<T>::GetValue() const typename ImprovedHoldAValue<T>::return_value ImprovedHoldAValue<T>::GetValue() const
{ {
return value_; return value_;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
And the result remain the same, albeit more efficient as a copy is avoided: And the result remain the same, albeit more efficient as a copy is avoided:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
ImprovedHoldAValue<int> hint(5); ImprovedHoldAValue<int> hint(5);
std::cout << hint.GetValue() << std::endl; std::cout << hint.GetValue() << std::endl;
ImprovedHoldAValue<std::string> sint("Hello world!"); ImprovedHoldAValue<std::string> sint("Hello world!");
std::cout << sint.GetValue() << std::endl; std::cout << sint.GetValue() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In fact sometimes you may even have **traits class**: class which sole purpose is to provide type informations! Such classes are often used as template parameters of other classes. In fact sometimes you may even have **traits class**: class which sole purpose is to provide type informations! Such classes are often used as template parameters of other classes.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Policies ## Policies
Policies are a way to provide a class for which a given aspect is entirely configurable by another class you provide as a template parameter. Policies are a way to provide a class for which a given aspect is entirely configurable by another class you provide as a template parameter.
STL uses up policies: for instance there is a second optional template parameter to `std::vector` which deals with the way to allocate the memory (and only with that aspect). So you may provide your own way to allocate the memory and provide it to `std::vector`, which will use it instead of its default behaviour. \cite{Alexandrescu2001} dedicates a whole chapter of his book to this example: he wrote an allocator aimed at being more efficient for the allocation of small objects. STL uses up policies: for instance there is a second optional template parameter to `std::vector` which deals with the way to allocate the memory (and only with that aspect). So you may provide your own way to allocate the memory and provide it to `std::vector`, which will use it instead of its default behaviour. \cite{Alexandrescu2001} dedicates a whole chapter of his book to this example: he wrote an allocator aimed at being more efficient for the allocation of small objects.
The syntax of a policy is a template class which also derives from at least one of its template parameter: The syntax of a policy is a template class which also derives from at least one of its template parameter:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class ColorPolicyT> template<class ColorPolicyT>
class Car : public ColorPolicyT class Car : public ColorPolicyT
{ }; { };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
struct Blue struct Blue
{ {
void Print() const void Print() const
{ {
std::cout << "My color is blue!" << std::endl; std::cout << "My color is blue!" << std::endl;
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <string> #include <string>
// Let's assume in the future a car provides a mechanism to change its color at will: // Let's assume in the future a car provides a mechanism to change its color at will:
class Changing class Changing
{ {
public: public:
void Display() const // I do not use `Print()` intentionally to illustrate there is no constraint void Display() const // I do not use `Print()` intentionally to illustrate there is no constraint
// but in true code it would be wise to use same naming scheme! // but in true code it would be wise to use same naming scheme!
{ {
std::cout << "Current color is " << color_ << "!" << std::endl; std::cout << "Current color is " << color_ << "!" << std::endl;
} }
void ChangeColor(const std::string& new_color) void ChangeColor(const std::string& new_color)
{ {
color_ = new_color; color_ = new_color;
} }
private: private:
std::string color_ = "white"; std::string color_ = "white";
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
Car<Blue> blue_car; Car<Blue> blue_car;
blue_car.Print(); blue_car.Print();
Car<Changing> future_car; Car<Changing> future_car;
future_car.Display(); future_car.Display();
future_car.ChangeColor("black"); future_car.ChangeColor("black");
future_car.Display(); future_car.Display();
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Variadic templates ## Variadic templates
If you have already written some C, you must be familiar with `printf`: If you have already written some C, you must be familiar with `printf`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <cstdio> #include <cstdio>
{ {
int i = 5; int i = 5;
double d = 3.1415; double d = 3.1415;
printf("i = %d\n", i); // Currently not printed due to Xeus-cling limitation... printf("i = %d\n", i); // Currently not printed due to Xeus-cling limitation...
printf("i = %d and d = %lf\n", i, d); printf("i = %d and d = %lf\n", i, d);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This function is atypical as it may take an arbitrary number of arguments. You can devise similar function of your own in C (look for `va_arg` if you insist...) but it was not recommended: under the hood it is quite messy, and limits greatly the checks your compiler may perform on your code. This function is atypical as it may take an arbitrary number of arguments. You can devise similar function of your own in C (look for `va_arg` if you insist...) but it was not recommended: under the hood it is quite messy, and limits greatly the checks your compiler may perform on your code.
C++ 11 introduced **variadic templates**, which provides a much neater way to provide this kind of functionality (albeit with a very tricky syntax: check all the `...` below... and it becomes worse if you need to propagate them). C++ 11 introduced **variadic templates**, which provides a much neater way to provide this kind of functionality (albeit with a very tricky syntax: check all the `...` below... and it becomes worse if you need to propagate them).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
// Overload when one value only. // Overload when one value only.
template<class T> template<class T>
void Print(T value) void Print(T value)
{ {
std::cout << value << std::endl; std::cout << value << std::endl;
} }
// Overload with a variadic number of arguments // Overload with a variadic number of arguments
template<class T, class ...Args> template<class T, class ...Args>
void Print(T value, Args... args) // args here will be all parameters passed to the function from the void Print(T value, Args... args) // args here will be all parameters passed to the function from the
// second one onward. // second one onward.
{ {
Print(value); Print(value);
Print(args...); // Will call recursively `Print()` with one less argument. Print(args...); // Will call recursively `Print()` with one less argument.
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
Print(5, "hello"); Print(5, "hello", "world");
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
Print("One"); Print("One");
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
Print(); // Compilation error: no arguments isn't accepted! Print(); // Compilation error: no arguments isn't accepted!
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To learn more about them, I recommend \cite{Meyers2015}, which provides healthy explanations about `std::forward` and `std::move` you will probably need soon if you want to use these variadic templates. To learn more about them, I recommend \cite{Meyers2015}, which provides healthy explanations about `std::forward` and `std::move` you will probably need soon if you want to use these variadic templates.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Template template parameters (not a mistake...) ## Template template parameters (not a mistake...)
You may want to be way more specific when defining a template parameter: instead of telling it might be whatever you want, you may impose that a specific template parameter should only be a type which is itself an instantiation of a template. You may want to be way more specific when defining a template parameter: instead of telling it might be whatever you want, you may impose that a specific template parameter should only be a type which is itself an instantiation of a template.
Let's consider a very dumb template function which purpose is to call print the value of `size()` for a STL container. We'll see them more extensively in a [dedicated notebook](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb), but for now you just have to know that these containers take two template parameters: Let's consider a very dumb template function which purpose is to call print the value of `size()` for a STL container. We'll see them more extensively in a [dedicated notebook](../5-UsefulConceptsAndSTL/3-Containers.ipynb), but for now you just have to know that these containers take two template parameters:
- One that describe the type inside the container (e.g. `double` for `std::vector<double>`). - One that describe the type inside the container (e.g. `double` for `std::vector<double>`).
- Another optional one which specifies how the memory is allocated. - Another optional one which specifies how the memory is allocated.
We could not bother and use directly a usual template parameter: We could not bother and use directly a usual template parameter:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<class ContainerT> template<class ContainerT>
void PrintSize1(const ContainerT& container) void PrintSize1(const ContainerT& container)
{ {
std::cout << container.size() << std::endl; std::cout << container.size() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
You may use it on seemlessly on usual STL containers: You may use it on seemlessly on usual STL containers:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <vector> #include <vector>
#include <list> #include <list>
#include <deque> #include <deque>
{ {
std::vector<double> vector { 3.54, -73.1, 1004. }; std::vector<double> vector { 3.54, -73.1, 1004. };
std::list<int> list { 15, -87, 12, 12, 0, -445 }; std::list<int> list { 15, -87, 12, 12, 0, -445 };
std::deque<unsigned int> deque { 2, 87, 95, 14, 451, 10, 100, 1000 }; std::deque<unsigned int> deque { 2, 87, 95, 14, 451, 10, 100, 1000 };
PrintSize1(vector); PrintSize1(vector);
PrintSize1(list); PrintSize1(list);
PrintSize1(deque); PrintSize1(deque);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
However, it would also work for any class that define a size parameters, regardless of its nature. However, it would also work for any class that define a size parameters, regardless of its nature.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <string> #include <string>
struct NonTemplateClass struct NonTemplateClass
{ {
std::string size() const std::string size() const
{ {
return "Might seem idiotic, but why not?"; return "Might seem idiotic, but why not?";
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
template<class U, class V, class W> template<class U, class V, class W>
struct TemplateWithThreeParameters struct TemplateWithThreeParameters
{ {
int size() const int size() const
{ {
return -99; return -99;
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
NonTemplateClass non_template_class; NonTemplateClass non_template_class;
TemplateWithThreeParameters<int, float, double> template_with_three_parameters; TemplateWithThreeParameters<int, float, double> template_with_three_parameters;
PrintSize1(non_template_class); PrintSize1(non_template_class);
PrintSize1(template_with_three_parameters); PrintSize1(template_with_three_parameters);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We see here with my rather dumb example that `PrintSize1()` also works for my own defined types, that are not following the expected prototype of a STL container (class with two template parameters). We see here with my rather dumb example that `PrintSize1()` also works for my own defined types, that are not following the expected prototype of a STL container (class with two template parameters).
It may seem pointless in this example, but the worst is that the method might be used to represent something entirely different from what we expect when we call `size()` upon a STL container. It may seem pointless in this example, but the worst is that the method might be used to represent something entirely different from what we expect when we call `size()` upon a STL container.
A possibility to limit the risk is to use a **template template parameter** in the function definition: A possibility to limit the risk is to use a **template template parameter** in the function definition:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
template<template <class, class> class ContainerT, class TypeT, class AllocatorT> template<template <class, class> class ContainerT, class TypeT, class AllocatorT>
void PrintSize2(const ContainerT<TypeT, AllocatorT>& container) void PrintSize2(const ContainerT<TypeT, AllocatorT>& container)
{ {
std::cout << container.size() << std::endl; std::cout << container.size() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
By doing so, we impose that the type of the argument is an instantiation of a class with two arguments. With that, STL containers work: By doing so, we impose that the type of the argument is an instantiation of a class with two arguments. With that, STL containers work:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <vector> #include <vector>
#include <list> #include <list>
#include <deque> #include <deque>
{ {
std::vector<double> vector { 3.54, -73.1, 1004. }; std::vector<double> vector { 3.54, -73.1, 1004. };
std::list<int> list { 15, -87, 12, 12, 0, -445 }; std::list<int> list { 15, -87, 12, 12, 0, -445 };
std::deque<unsigned int> deque { 2, 87, 95, 14, 451, 10, 100, 1000 }; std::deque<unsigned int> deque { 2, 87, 95, 14, 451, 10, 100, 1000 };
// At call site, you don't have to specify the template arguments that are inferred. // At call site, you don't have to specify the template arguments that are inferred.
PrintSize2(vector); PrintSize2(vector);
PrintSize2(list); PrintSize2(list);
PrintSize2(deque); PrintSize2(deque);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
whereas my own defined types don't: whereas my own defined types don't:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
NonTemplateClass non_template_class; NonTemplateClass non_template_class;
TemplateWithThreeParameters<int, float, double> template_with_three_parameters; TemplateWithThreeParameters<int, float, double> template_with_three_parameters;
PrintSize2(non_template_class); PrintSize2(non_template_class);
PrintSize2(template_with_three_parameters); PrintSize2(template_with_three_parameters);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In practice you shouldn't need to use that too often, but as the topics in this notebook it is worthy to know the possibility exists (that may help you understand an error message should you use a library using them). I had to resort to them a couple of times, especially along with policies. In practice you shouldn't need to use that too often, but as the topics in this notebook it is worthy to know the possibility exists (that may help you understand an error message should you use a library using them). I had to resort to them a couple of times, especially along with policies.
If you want to learn more about them, you should really read \cite{Alexandrescu2001}). If you want to learn more about them, you should really read \cite{Alexandrescu2001}).
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# References # References
(<a id="cit-Alexandrescu2001" href="#call-Alexandrescu2001">Alexandrescu, 2001</a>) Andrei Alexandrescu, ``_Modern C++ Design: Generic Programming and Design Patterns applied_'', 01 2001. (<a id="cit-Alexandrescu2001" href="#call-Alexandrescu2001">Alexandrescu, 2001</a>) Andrei Alexandrescu, ``_Modern C++ Design: Generic Programming and Design Patterns applied_'', 01 2001.
(<a id="cit-Meyers2015" href="#call-Meyers2015">Meyers, 2015</a>) Scott Meyers, ``_Effective modern C++: 42 specific ways to improve your use of C++11 (<a id="cit-Meyers2015" href="#call-Meyers2015">Meyers, 2015</a>) Scott Meyers, ``_Effective modern C++: 42 specific ways to improve your use of C++11
and C++14_'', 2015. [online](http://www.worldcat.org/oclc/890021237) and C++14_'', 2015. [online](http://www.worldcat.org/oclc/890021237)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_ © _CNRS 2016_ - _Inria 2018-2020_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_ _This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_ _The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment