web-dev-qa-db-fra.com

Quelle est la logique derrière le mot clé "using" en C ++?

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
100
user3111311

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 nom Vec est un nom pour la famille std::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 phrase

template<class T>
    using Vec = std::vector<T, MyAllocator<T> >;

peut être lu/interprété comme: à partir de maintenant, j’utiliserai Vec<T> comme synonyme de std::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:

  • alias de template (ou typedefs de template, le premier est préféré nomwise)
  • alias ​​de l'espace de noms (c'est-à-dire, namespace PO = boost::program_options et using PO = ... équivalent)
  • le document dit 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.
  • apportant quelque chose dans la portée (par exemple, namespace std dans la portée globale), fonctions membres, héritage des constructeurs

Il 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);
85
user1508519