Quelle est la logique derrière le mot clé "using" en C++?
Il est utilisé dans différentes situations et j'essaie de trouver si tous ceux qui ont quelque chose en commun et il y a une raison pour laquelle le mot-clé "using" est utilisé en tant que tel.
using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class
En C++ 11, le mot clé using
utilisé pour type alias
Est identique à typedef
.
7.1.3.2
Un nom de typedef peut également être introduit par une déclaration d'alias. L'identifiant suivant le mot-clé using devient un nom de typedef et le spécificateur d'attribut-specifier-seq optionnel suivant l'identificateur appartenant à ce nom de typedef. Elle a la même sémantique que si elle était introduite par le spécificateur typedef. En particulier, il ne définit pas un nouveau type et il ne doit pas apparaître dans le type-id.
Bjarne Stroustrup fournit un exemple pratique:
typedef void (*PFD)(double); // C style
using PF = void (*)(double); // using plus C-style type
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP
Avant C++ 11, le mot clé using
peut amener des fonctions membres à la portée. En C++ 11, vous pouvez maintenant le faire pour les constructeurs (un autre exemple de Bjarne Stroustrup):
class Derived : public Base {
public:
using Base::f; // lift Base's f into Derived's scope -- works in C++98
void f(char); // provide a new f
void f(int); // prefer this f to Base::f(int)
using Base::Base; // lift Base constructors Derived's scope -- C++11 only
Derived(char); // provide a new constructor
Derived(int); // prefer this constructor to Base::Base(int)
// ...
};
Ben Voight fournit une bonne raison de ne pas introduire un nouveau mot clé ou une nouvelle syntaxe. La norme veut éviter autant que possible de casser l'ancien code. C'est pourquoi, dans les documents de proposition, vous verrez des sections telles que Impact on the Standard
, Design decisions
Et leur incidence sur les anciens codes. Il y a des situations où une proposition semble être une très bonne idée mais qui pourrait ne pas avoir de succès, car elle serait trop difficile à mettre en œuvre, trop confuse ou serait en contradiction avec l'ancien code.
Voici un vieux papier de 2003 n1449 . La justification semble être liée aux modèles. Attention: il peut y avoir des erreurs de frappe dues à la copie depuis un fichier PDF.
Tout d’abord, considérons un exemple de jouet:
template <typename T> class MyAlloc {/*...*/}; template <typename T, class A> class MyVector {/*...*/}; template <typename T> struct Vec { typedef MyVector<T, MyAlloc<T> > type; }; Vec<int>::type p; // sample usage
Le problème fondamental de cet idiome, et le principal facteur de motivation de cette proposition, est que cet idiome fait en sorte que les paramètres du modèle apparaissent dans un contexte non déductible. Autrement dit, il ne sera pas possible d'appeler la fonction foo ci-dessous sans spécifier explicitement les arguments du modèle.
template <typename T> void foo (Vec<T>::type&);
Donc, la syntaxe est un peu moche. Nous préférons éviter les
::type
Imbriqués. Nous préférerions quelque chose comme ce qui suit:template <typename T> using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below Vec<int> p; // sample usage
Notez que nous évitons spécifiquement le terme "modèle typedef" et introduisons la nouvelle syntaxe impliquant le couple "using" et "=" pour éviter toute confusion: nous ne définissons aucun type ici, nous introduisons un synonyme (ie alias) pour une abstraction d'un type-id (expression de type) impliquant des paramètres de modèle. Si les paramètres de modèle sont utilisés dans des contextes déductibles dans l'expression de type, chaque fois que l'alias de modèle est utilisé pour former un modèle-id, les valeurs des paramètres de modèle correspondants peuvent être déduites. Vous en apprendrez plus à ce sujet. Dans tous les cas, il est maintenant possible d'écrire des fonctions génériques qui fonctionnent sur
Vec<T>
Dans un contexte déductible, et la syntaxe est également améliorée. Par exemple, nous pourrions réécrire foo comme suit:template <typename T> void foo (Vec<T>&);
Nous soulignons ici que l’une des principales raisons de proposer des alias de modèles était que la déduction d’arguments et l’appel à
foo(p)
aboutissent.
Le papier suivant n1489 explique pourquoi using
au lieu d'utiliser typedef
:
Il a été suggéré de (ré) utiliser le mot-clé typedef - comme dans l'article [4] - pour introduire des alias de modèles:
template<class T> typedef std::vector<T, MyAllocator<T> > Vec;
Cette notation présente l’avantage d’utiliser un mot-clé déjà connu pour introduire un alias de type. Cependant, il présente également plusieurs inconvénients, parmi lesquels la confusion d'utiliser un mot clé connu pour introduire un alias pour un nom de type dans un contexte où l'alias ne désigne pas un type, mais un modèle;
Vec
n'est pas un alias pour un type et ne doit pas être pris pour un nom de type. Le nomVec
est un nom pour la famillestd::vector< [bullet] , MyAllocator< [bullet] > >
- où la puce est un espace réservé pour un nom de type. Par conséquent, nous ne proposons pas la syntaxe "typedef". Par contre la phrasetemplate<class T> using Vec = std::vector<T, MyAllocator<T> >;
peut être lu/interprété comme: à partir de maintenant, j’utiliserai
Vec<T>
comme synonyme destd::vector<T, MyAllocator<T> >
. Avec cette lecture, la nouvelle syntaxe pour le crénelage semble raisonnablement logique.
Je pense que la distinction importante est faite ici, alias es à la place de type s. Une autre citation du même document:
Une déclaration d'alias est une déclaration et non une définition. Une déclaration d'alias introduit un nom dans une région déclarative en tant qu'alias du type désigné par le côté droit de la déclaration. Le cœur de cette proposition concerne les alias de noms de types, mais la notation peut évidemment être généralisée pour fournir des orthographes alternatives à l'alias d'espace de noms ou à un ensemble de noms de fonctions surchargées (voir § 2.3 pour une analyse plus détaillée). [Ma note: Cette section explique à quoi cette syntaxe peut ressembler et les raisons pour lesquelles elle ne fait pas partie de la proposition.] Il est à noter que la déclaration d'alias de production grammaticale est acceptable partout où une déclaration typedef ou une définition d'alias de nom d'espace de nom est acceptable.
Résumé, pour le rôle de using
:
namespace PO = boost::program_options
et using PO = ...
équivalent)A typedef declaration can be viewed as a special case of non-template alias-declaration
. C'est un changement esthétique, et est considéré identique dans ce cas.namespace std
dans la portée globale), fonctions membres, héritage des constructeursIl ne peux pas être utilisé pour:
int i;
using r = i; // compile-error
Au lieu de faire:
using r = decltype(i);
Nommer un ensemble de surcharges.
// bring cos into scope
using std::cos;
// invalid syntax
using std::cos(double);
// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);