Quelques raccourcis :
Chaque langage a son système de types, du très simple au très complexe, du très rigide au très souple, du très sécuritaire au très... divertissant.
Les systèmes de types varient selon les langages de programmation, sans surprises.
À propos du système de types de C# :
À propos du système de types de C++ :
Voir aussi les références sur les rvalue, une particularité de C++ 11 ouvrant la porte à une gamme d'optimisations importantes.
La question des rvalue et des lvalue, en particulier dans les langages C et en C++ :
Depuis C++ 11, la dichotomie lvalue / rvalue ne suffit plus à exprimer toutes les nuances de valeurs du système de types de ce langage. Une documentation très convenable sur le sujet est disponible sur http://en.cppreference.com/w/cpp/language/value_category mais en résumé, on trouve les catégories « pures » et les catégories « mixtes », de même que quelques cas étranges qui sont plus difficiles à catégoriser.
Pour comprendre ce qui suit, il faut savoir qu'un objet en C++ est une zone de mémoire capable d'entreposer une valeur, pas nécessairement l'instanciation d'une classe.
Ce qu'on nomme une lvalue, techniquement, est une expression qui identifie un objet non-temporaire ou une fonction globale. Ceci inclut les const, même si elles ne peuvent pas être à gauche d'une affectation une fois initialisées. Avec une lvalue, on peut faire tout ce qu'on peut aussi faire avec une glvalue (plus bas); on peut aussi prendre son adresse, lui affecter une valeur (si elle est modifiable) et initialiser une référence sur une lvalue. Les transtypages peuvent exprimer une lvalue, dans la mesure où le type de destination est une lvalue.
Ce qu'on nomme depuis C++ 11 une prvalue (Pure rvalue), et qui correspond à la définition traditionnelle (mais désormais inadéquate) d'une rvalue, est une expression qui identifie une valeur temporaire qui n'est pas associée à un objet. Les littéraux, par exemple, font partie de cette catégorie, et il en va de même pour les résultats des appels de fonction. Une prvalue est comme une rvalue (plus bas); de plus, sont type dynamique est le même que son type statique (elle ne prête pas au polymorphisme dynamique), elle ne peut être const ou volatile, et ne peut être d'un type incomplet.
Ce qu'on nomme une xvalue est un objet sur le point d'expirer, donc un objet sur lequel le mouvement peut s'appliquer. Une xvalue se comporte comme une rvalue (plus bas) et comme une glvalue (plus bas); elle permet le polymorphisme dynamique), elle peut être qualifiée const ou volatile.
Ce qu'on nomme une glvalue (Generalized lvalue) est une expression qui peut être une lvalue ou une xvalue. Une glvalue se comporte comme une lvalue traditionelle (C++ 03 et moins); de plus, une glvalue peut être comvertie implicitement en prvalue par une conversion de lvalue à rvalue, une conversion de tableau à pointeur, ou une conversion de fonction à pointeur. Une glvalue permet le polymorphisme dynamique et peut être d'un type incomplet si le contexte le permet.
Ce qu'on nomme maintenant une rvalue est est une expression qui peut être une prvalue ou une xvalue. On ne peut prendre son adresse, on ne peut lui affecter une valeur, on peut par contre l'utiliser pour initialiser une référence const sur une lvalue (cela provoquera une copie silencieuse) ou pour initialiser une références sur les rvalue.
Quelques cas « étranges » échappent aux définitions ci-dessus.
En 2017, Dart se donne un système de types, comme l'explique David Morgan : https://medium.com/dartlang/dart-gets-a-type-system-6bd3121772de
À propos du système de types de Haskell :
À propos du système de types de Kotlin :
À propos du système de types de Python :
À propos du système de types de Swift :
Un concept fort utile entre autres pour implémenter des techniques se raffinant conceptuellement avec la progression d'une structure d'héritage en programmation orientée objet (pensez au clonage, entre autres).
Exemples de votre humble serviteur :
class B
{
// ...
public:
virtual void service_important(int);
virtual ~B() = default;
};
class D
: public B
{
// ...
public:
void service_specialise(int);
};
int main()
{
using pmethB = void (B::*)(int);
using pmethD = void (D::*)(int);
pmethB pmb0 = &B::service_important; // Ok
// pmethB pmb1 = &D::service_specialise; // illégal
pmethD pmd0 = &B::service_important; // Ok
pmethD pmd1 = &D::service_important; // Ok
}