Commit 15c2f798 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Cleaning-up template part.

parent 3e380de7
%% 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:
* [Introduction to the concept of templates](/notebooks/4-Templates/1-Intro.ipynb)
* [TP 12](/notebooks/4-Templates/1b-TP.ipynb)
* [Specialization](/notebooks/4-Templates/2-Specialization.ipynb)
* [Special syntax: typename, template and mandatory this](/notebooks/4-Templates/3-Syntax.ipynb)
* [TP 13](/notebooks/4-Templates/3b-TP.ipynb)
* [Metaprogramming](/notebooks/4-Templates/4-Metaprogramming.ipynb)
* [Hints to more advanced concepts with templates](/notebooks/4-Templates/5-MoreAdvanced.ipynb)
* [Introduction to the concept of templates](./1-Intro.ipynb)
* [TP 12](./1b-TP.ipynb)
* [Specialization](./2-Specialization.ipynb)
* [Special syntax: typename, template and mandatory this](./3-Syntax.ipynb)
* [TP 13](./3b-TP.ipynb)
* [Metaprogramming](./4-Metaprogramming.ipynb)
* [Hints to more advanced concepts with templates](./5-MoreAdvanced.ipynb)
%% 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/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
This diff is collapsed.
%% 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:
<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>
%% Cell type:markdown id: tags:
## Total specialization
### 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...
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:
``` C++17
#include <iostream>
template<class T>
T Convert(int value)
{
std::cout << "Generic instantiation called!" << std::endl;
return static_cast<T>(value);
}
```
%% Cell type:code id: tags:
``` C++17
#include <string>
template<> // notice the empty brackets!
std::string Convert<std::string>(int value)
{
std::cout << "std::string instantiation called!" << std::endl;
return std::to_string(value);
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
Convert<double>(5);
Convert<std::string>(5);
}
```
%% 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:
%% Cell type:code id: tags:
``` C++17
class HoldAnInt
{
public:
explicit HoldAnInt(int value);
private:
int value_;
};
```
%% Cell type:code id: tags:
``` C++17
HoldAnInt::HoldAnInt(int value)
: value_(value)
{ }
```
%% Cell type:code id: tags:
``` C++17
template<>
HoldAnInt Convert(int value)
{
std::cout << "HoldAnInt instantiation called!" << std::endl;
return HoldAnInt(value);
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
Convert<double>(5);
Convert<std::string>(5);
Convert<HoldAnInt>(5);
}
```
%% Cell type:markdown id: tags:
### For template classes
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)
* Or just a method.
The case below provides entire class specialization for `double` and just method specialization for `std::string`:
%% Cell type:code id: tags:
``` C++17
// First the generic class `HoldAValue`
#include <iostream>
#include <string>
template<class T>
class HoldAValue
{
public:
HoldAValue(T value);
T GetValue() const;
private:
T value_;
};
template<class T>
HoldAValue<T>::HoldAValue(T value)
: value_(value)
{ }
template<class T>
T HoldAValue<T>::GetValue() const
{
return value_;
}
```
%% Cell type:code id: tags:
``` C++17
// Method specialization for std::string
template<>
std::string HoldAValue<std::string>::GetValue() const
{
return "String case (through method specialization): " + value_;
}
```
%% Cell type:code id: tags:
``` C++17
// Class specialization for double
template<>
class HoldAValue<double>
{
public:
HoldAValue(std::string blah, double value);
double GetValue() const;
private:
std::string blah_;
double value_;
};
HoldAValue<double>::HoldAValue(std::string blah, double value)
: blah_(blah),
value_(value)
{ }
double HoldAValue<double>::GetValue() const
{
std::cout << blah_;
return value_;
}
```
%% Cell type:code id: tags:
``` C++17
{
HoldAValue<int> integer(5);
std::cout << integer.GetValue() << std::endl;
HoldAValue<std::string> string("Hello world");
std::cout << string.GetValue() << std::endl;
HoldAValue<double> dbl("Double case (through class specialization): ", 3.14);
std::cout << dbl.GetValue() << std::endl;
}
```
%% Cell type:markdown id: tags:
## Partial specialization
### For classes
%% Cell type:markdown id: tags:
It is also possible for classes to **specialize partially** a template class:
%% Cell type:code id: tags:
``` C++17
template<class TypeT, std::size_t Nelts>
class MyArray
{
public:
explicit MyArray(TypeT initial_value);
private:
TypeT content_[Nelts];
};
```
%% Cell type:code id: tags:
``` C++17
template<class TypeT, std::size_t Nelts>
MyArray<TypeT, Nelts>::MyArray(TypeT initial_value)
{
for (auto i = 0ul; i < Nelts; ++i)
content_[i] = initial_value;
}
```
%% Cell type:code id: tags:
``` C++17
template<class TypeT>
class MyArray<TypeT, 10ul>
{
public:
explicit MyArray(TypeT initial_value);
private:
TypeT content_[10ul];
};
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
template<class TypeT>
MyArray<TypeT, 10ul>::MyArray(TypeT initial_value)
{
std::cout << "Partial specialization constructor: type is still templated but size is fixed " << std::endl;
for (auto i = 0ul; i < 10ul; ++i)
content_[i] = initial_value;
}
```
%% Cell type:code id: tags:
``` C++17
{
MyArray<int, 10ul> array1(2);
}
```
%% Cell type:markdown id: tags:
### But not for functions!
However, partial specialization is **forbidden** for template functions:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
template<class T, class U>
void PrintSquare(T t, U u)
{
std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
{
PrintSquare(5, 7.);
}
```
%% Cell type:code id: tags:
``` C++17
#include <string>
template<class T>
void PrintSquare<T, int>(T t, std::string u)
{
std::cout << "Partial function specialization: doesn't compile!" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
}
```
%% Cell type:markdown id: tags:
You might have the impress it works if you try defining the function without specifying the brackets:
%% Cell type:code id: tags:
``` C++17
#include <string>
template<class T>
void PrintSquare(T t, std::string u)
{
std::cout << "Seamingly ok function template specialization " << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
// May fail to Xeus-cling; but expected behaviour is to 'work' but not as intended...
{
std::string hello("Hello");
PrintSquare(5., hello);
}
```
%% Cell type:markdown id: tags:
but you actually did simply an overload of `Print()`; calling the explicit specialization fails:
%% Cell type:code id: tags:
``` C++17
{
PrintSquare<double, std::string>(5., std::string("Hello"));
}
```
%% 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.
### Mimicking the partial template specialization for functions
#### 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...
%% Cell type:code id: tags:
``` C++17
#include <iostream>
template<class T, class U>
struct PrintSquareHelper
{
static void Do(T t, U u)
{
std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
}
};
```
%% Cell type:code id: tags:
``` C++17
#include <string>
template<class T>
struct PrintSquareHelper<T, std::string>
{
static void Do(T t, std::string u)
{
std::cout << "Specialized instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
}
};
```
%% Cell type:code id: tags:
``` C++17
template<class T, class U>
void PrintSquare2(T t, U u)
{
PrintSquareHelper<T, U>::Do(t, u);
}
```
%% Cell type:code id: tags:
``` C++17
{
PrintSquare2<double, std::string>(5., std::string("Hello"));
}
```
%% Cell type:markdown id: tags:
### If constexpr
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):
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <string>
template<class T, class U>
void Print3(T t, U u)
{
if constexpr (std::is_same<U, std::string>())
{
std::cout << "Specialized instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
}
else
{
std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
}
}
```
%% Cell type:code id: tags:
``` C++17
{
Print3<double, std::string>(5., "hello");
}
```
%% Cell type:markdown id: tags:
**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:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
template<class T, class U>
void Print4(T t, U u)
{
if (std::is_same<U, std::string>())
{
std::cout << "Specialized instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << (u + u) << ")" << std::endl;
}
else
{
std::cout << "Generic instantiation" << std::endl;
std::cout << "(t^2, u^2) = (" << t * t << ", " << u * u << ")" << std::endl;
}
}
```
%% Cell type:code id: tags:
``` C++17
{
Print4<double, std::string>(5., "Hello"); // Compilation error!
}
```
%% 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/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% 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:
<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>
%% 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.
## Mandatory `this` in calling base class methods
%% Cell type:code id: tags:
``` C++17
template<class T>
class Base
{
public:
Base() = default;
void BaseMethod()
{ }
};
```
%% Cell type:code id: tags:
``` C++17
template<class T>
class Derived : public Base<T>
{
public:
Derived() = default;
void DerivedMethod();
};
```
%% Cell type:code id: tags:
``` C++17
template<class T>
void Derived<T>::DerivedMethod()
{
BaseMethod(); // Compilation error!
}
```
%% 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.
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:
``` C++17
template<class T>
void Derived<T>::DerivedMethod()
{
this->BaseMethod(); // Now ok!
}
```
%% 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).
%% Cell type:code id: tags:
``` C++17
template<class T>
class Derived2 : public Base<T>
{
public:
Derived2() = default;
using parent = Base<T>;
void DerivedMethod()
{
parent::BaseMethod(); // also ok!