Commit 22b6b1a4 authored by ROUVREAU Vincent's avatar ROUVREAU Vincent
Browse files

Fix 'at bay', clarify where to find traits in cppreference

parent 15282ca4
%% Cell type:markdown id: tags:
# [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><ul class="toc-item"><li><span><a href="#[optional]-A-bit-of-wandering:-how-will-C++-20-concepts-will-help-to-enforce-the-constraint-upon-the-template-parameter" data-toc-modified-id="[optional]-A-bit-of-wandering:-how-will-C++-20-concepts-will-help-to-enforce-the-constraint-upon-the-template-parameter-2.1">[optional] A bit of wandering: how will C++ 20 concepts will help to enforce the constraint upon the template parameter</a></span></li></ul></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 to 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!
}
};
```
%% Cell type:markdown id: tags:
## 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 in the `Member types` section.
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:
``` C++17
#include <vector>
{
std::vector<int>::value_type i = 5; // convoluted way to just say int i = 5!
}
```
%% Cell type:markdown id: tags:
However, this simple construct fails in the following case:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
template<class ContainerT>
void PrintFive()
{
std::cout << static_cast<ContainerT::value_type>(5) << std::endl;
}
```
%% 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_).
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 it 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:
``` C++17
#include <iostream>
template<class ContainerT>
void PrintFive()
{
std::cout << static_cast<typename ContainerT::value_type>(5) << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
PrintFive<int>(); // Won't compile: no trait 'value_type' for an 'int'!
```
%% Cell type:code id: tags:
``` C++17
PrintFive<std::vector<int>>(); // Ok!
```
%% Cell type:markdown id: tags:
### [optional] A bit of wandering: how will C++ 20 concepts will help to enforce the constraint upon the template parameter
Once again, C++ 20 concept will be much appreciated here to provide more straightforward error messages (see [@Coliru](https://coliru.stacked-crooked.com/a/4cc87e5a41d1a94c)):
%% Cell type:code id: tags:
``` C++17
// C++ 20 code - run it in Coliru!
#include <iostream>
#include <vector>
#include <list>
template<typename T>
concept HasValueType = requires(T a)
{
typename T::value_type;
};
template<class ContainerT>
requires HasValueType<ContainerT>
void PrintFive()
{
std::cout << static_cast<typename ContainerT::value_type>(5) << std::endl;
}
int main(int argc, char** argv)
{
PrintFive<std::vector<int>>();
PrintFive<std::list<int>>();
// PrintFive<int>(); // COMPILATION ERROR! But with a clear error message.
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
The error message (with gcc) includes all the relevant information fo understand what is wrong the the `int` case:
````
main.cpp: In function 'int main(int, char**)':
main.cpp:27:20: error: use of function 'void PrintFive() [with ContainerT = int]' with unsatisfied constraints
27 | PrintFive<int>(); // COMPILATION ERROR! But with a clear error message.
...
main.cpp:8:14: note: the required type 'typename T::value_type' is invalid
8 | typename T::value_type;
````
%% Cell type:markdown id: tags:
## Mandatory `template` keyword
%% 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 involved. For instance:
%% Cell type:code id: tags:
``` C++17
struct Foo
{
template<int N>
void PrintN() const;
};
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
template<int N>
void Foo::PrintN() const
{
std::cout << N << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
template<class U>
void Print(const U& u)
{
std::cout << u.PrintN<0>() << std::endl;
}
```
%% 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.
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:
``` C++17
template<class U>
void Print(const U& u)
{
std::cout << u.template PrintN<0>() << std::endl; // Notice the template keyword!
}
```
%% Cell type:markdown id: tags:
## Good practice: always check obvious template instantiations in test!
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:
``` C++17
{
Foo foo;
Print(foo); // Compilation error!
}
```
%% 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`:
* `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.
`Foo` doesn't check the second condition... but you can't really see it without checking it (once again C++ 20 concepts will help on that matter, but the advise here still stands).
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:
## No virtual template method!
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.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2021_
_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)_
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment