Closed GuillaumeBelz closed 3 years ago
Lire l’article : https://www.fluentcpp.com/2021/05/22/the-subtle-dangers-of-temporaries-in-for-loops/
Le site est down
Lire l’article : https://belaycpp.com/2021/06/01/a-list-of-bad-practices-commonly-seen-in-industrial-projects/
Voici un article qui se lit facilement, sur les mauvaises pratiques que l'on rencontre régulièrement dans des projets industriels. Il n'y a rien de nouveau, mais c'est bien de faire des rappels de temps en temps.
static
(utilises un espace de noms) ou une classe avec principalement que des getters et setters (fais une structure et des attributs publiques).Si tu veux en savoir plus sur la qualité du code, je te conseille les livres de C. Martin (Clean code, Clean coder, Clean architecture).
Lire l’article : https://herbsutter.com/2021/06/03/gotw-102-solution-assertions-and-ub-difficulty-7-10/
Herb Sutter est l'un des experts C++ les plus influents : membre du comité de normalisation du C++, auteur de plusieurs livres très connus, speaker dans plusieurs conférences (je te conseille ses talks à la CppCon), développeur chez Microsoft.
Cet article est un Guru of the Week (GotW). Les GotW sont des articles de blog qu'il écrit depuis des dizaines d'années. Le principe est de poser une série de questions sur un thématique dans un premier article, les lecteurs apportent des éléments de réponse aux questions, puis un second article "GotW Solution" présente les explications.
Cette article est la solution du GotW 102 : https://herbsutter.com/2021/05/25/gotw-102-assertions-and-ub-difficulty-7-10/
Est "ce qu'il se passe lorsque votre programme essaie de faire quelque chose dont le sens n'est pas du tout défini dans le langage ou la bibliothèque standard C++ (code et/ou données illégaux)". Le compilateur peut faire ce qu'il veut.
Est "ce qu'il se passe lorsque votre programme fait quelque chose pour lequel la norme C++ ne documente pas les résultats". Le résultat est valide mais tu ne sais pas à l'avance ce qu'il sera.
Dépend du compilateur.
Exemple avec le déréférencement d'un pointeur null.
void deref_and_set( int* p ) {
assert( p );
*p = 42;
}
Exemple avec le calcul d'un point médian.
int midpoint( int low, int high ) {
assert( low <= high );
return low + (high-low)/2;
// less overflow-prone than “(low+high)/2”
// more accurate than “low/2 + high/2”
}
Guideline : un comportement non spécifié peut devenir un comportement indéfini si tu utilises le résultat sans vérifier que le résultat retourné ne produise pas un comportement indéfini.
Guideline : ne spécifie pas le comportement de ta fonction (postconditions) pour les entrée invalides (préconditions).
Dans le code d'exemple suivant, le comportement de la fonction ne devrait pas être valide si x est nul (assert). Mais un test est ajouté (programmation défensive) pour que le comportement soit quand même valide en release si l'utilisateur de la fonction fait une erreur.
some_result_value DoSomething( int x ) {
assert( x != 0 );
if ( x == 0 ) { return error_value; }
return sensible_result(x);
}
Guideline : ne pas respecter une assertion n'est pas nécessairement un comportement indéfini.
Guideline : toujours documenter les contraintes sur les entrées de fonction (préconditions). L'utilisateur de la fonction doit savoir quelles valeurs d'entrée sont invalides.
Guideline : toujours respecter les préconditions quand tu utilises une fonction.
Lire l’article : https://www.cppstories.com/2019/08/17smallercpp17features/
Le titre de cet article est explicite : il présente 17 fonctionnalités mineurs mais utiles du C++17. Quelques fonctionnalités concernent le langage, mais la grande majorité concerne la bibliothèque standard. Les fonctionnalités majeures (structured bindings, filesystem, parallel algorithms, if constexpr, std::optional, std::variant, etc.) ne sont pas abordées ici.
new
Par exemple pour les données SIMD. Dans le code suivant, en C++11/14, l'alignement n'est pas garantie. En C++17, l'alignement est garantie.
class alignas(16) vec4
{
float x, y, z, w;
};
auto pVectors = new vec4[1000];
inline
en C++17class MyClass {
static inline std::string startName = "Hello World";
};
__has_include
Pour vérifier si un header a déjà été inclus ou pas.
#if defined __has_include
# if __has_include(<charconv>)
# define has_charconv 1
# include <charconv>
# endif
#endif
En C++14, le suffixe _t
a été ajouté pour les traits pour remplacer ::type
. En C++17, le suffixe _v
est ajouté pour remplacer ::value
.
std::is_integral_v<T>
std::is_class_v<T>
template<class... B> struct conjunction;
template<class... B> struct disjunction;
template<class B> struct negation;
#include<type_traits>
template<typename... Ts>
std::enable_if_t<std::conjunction_v<std::is_same<int, Ts>...> >
PrintIntegers(Ts ... args) {
(std::cout << ... << args) << '\n';
}
std::void_t
Ce type peut être utile dans certaines implémentation de méta-fonctions.
template< class... >
using void_t = void;
std::from_chars
Bas niveau et rapide, mais nécessite un peu plus de code.
const std::string str { "12345678901234" };
int value = 0;
const auto res = std::from_chars(str.data(), str.data() + str.size(), value);
Permet de simplifier et optimiser le déplacement des noeuds.
inSet.emplace("John");
auto handle = inSet.extract("John");
outSet.insert(std::move(handle));
try_emplace()
dans std::map
et std::unordered_map
emplace
prend les arguments, même si l'insertion échoue, alors que try_emplace
ne fait rien en cas d'échec.
std::map<std::string, std::string> m;
m["Hello"] = "World";
m.emplace("Hello", std::move(s)); // échec de l'insertion
// s a été move après emplace
m.try_emplace("Hello", std::move(s)); // échec de l'insertion
// s n'a pas été move après try_emplace
insert_or_assign()
dans std::map
et std::unordered_map
insert
n'ajoute pas l'élément si la clé existe déjà, insert_or_assign
ajoute l'élément dans tous les cas.
myMap.insert_or_assign("c", "cherry"); // map contient "cherry"
myMap.insert_or_assign("c", "clementine"); // map contient "clementine"
emplace
retourne le nouvel élément ajouté// since C++11 and until C++17 for std::vector
template< class... Args >
void emplace_back( Args&&... args );
// since C++17 for std::vector
template< class... Args >
reference emplace_back( Args&&... args );
std::sample
Pour extraire aléatoirement n éléments.
Par exemple gcd()
, lcm()
, clamp()
, etc.
std::shared_ptr
Avant, c'était possible uniquement avec std::unique_ptr
.
std::shared_ptr<int[]> ptr(new int[10]);
std::scoped_lock
Pour lock plusieurs mutex en même temps.
std::scoped_lock lck(first_mutex, second_mutex);
std::invoke
Pour invoquer des fonctions.
// a regular function:
std::cout << std::invoke(foo, 10, 12) << '\n';
// a lambda:
std::cout << std::invoke([](double d) { return d*10.0;}, 4.2) << '\n';
std::auto_ptr
Enfin !
unary_function()
, binary_function(),
ptr_fun()`, etc.
Lire l’article : https://itnext.io/c-vs-rust-simple-polymorphism-comparison-e4d16024b57
L'auteur de cet article compare comment le polymorphisme est implémenté en C++ et en Rust. Le propos du polymorphisme est de pouvoir accéder à un type concret, dérivé d'un type de base, à l'éxecution, via un pointeur ou une référence.
Par exemple, des classes Dog
et Cat
qui dérivent de Animal
.
Le code d'exemple :
#include <iostream>
#include <memory>
#include <vector>
class Animal {
public:
virtual ~Animal() = default;
virtual void talk() const = 0;
};
class Dog final : public Animal {
public:
void talk() const override { std::clog << "I'm a dog\n"; }
};
class Cat final : public Animal {
public:
void talk() const override { std::clog << "I'm a cat\n"; }
};
int main() {
std::vector<std::unique_ptr<Animal>> animals;
animals.emplace_back(std::make_unique<Dog>());
animals.emplace_back(std::make_unique<Cat>());
for (const auto& animal: animals) {
animal->talk();
}
}
Affiche :
I'm a dog
I'm a cat
La version Rust :
trait Animal {
fn talk(&self);
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn talk(&self) {
println!("I'm a dog");
}
}
impl Animal for Cat {
fn talk(&self) {
println!("I'm a cat");
}
}
fn main() {
let animals : Vec<Box<dyn Animal>> = vec![Box::new(Dog{}), Box::new(Cat{})];
for animal in animals.iter() {
animal.talk();
}
}
Les différences entre les versions C++ et Rust :
Animal
est implémentée sous forme d'un trait et les classes Dog
et Cat
implémentent ce trait.&self
.Dog
et Cat
sont vides et ne font rien. Tout se passe via les implémentations de la fonction talk()
dans le trait Animal
.Box
est équivalent à std::unique_ptr
, dyn
indique que l'allocation est dynamique.
Cf l'issue precedente pour les idees d'articles https://github.com/NotANameServer/discord/issues/26
A publier le 30 mai