¿Cuál es la lógica detrás de la palabra clave “usar” en C ++?

¿Cuál es la lógica detrás de la palabra clave “usar” en C ++?

Se usa en diferentes situaciones y estoy tratando de encontrar si todas tienen algo en común y hay una razón por la cual la palabra clave “usar” se usa como tal.

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, la palabra clave using cuando se utiliza para type alias es idéntica a typedef .

7.1.3.2

Un typedef-name también puede ser introducido por una statement de alias. El identificador que sigue a la palabra clave using se convierte en un typedef-name y el atributo-especificador-seq opcional que sigue al identificador pertenece a ese typedef-name. Tiene la misma semántica que si fuera introducida por el especificador typedef. En particular, no define un nuevo tipo y no aparecerá en el ID de tipo.

Bjarne Stroustrup proporciona un ejemplo práctico:

 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 

Pre-C ++ 11, la palabra clave using puede llevar las funciones miembro al scope. En C ++ 11, ahora puede hacer esto para los constructores (otro ejemplo 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 proporciona una buena razón detrás de la lógica de no presentar una nueva palabra clave o una nueva syntax. La norma quiere evitar romper el código antiguo tanto como sea posible. Esta es la razón por la que en los documentos de propuesta verá secciones como Impact on the Standard , Design decisions y cómo podrían afectar el código anterior. Hay situaciones en las que una propuesta parece una buena idea, pero puede no tener tracción porque sería demasiado difícil de implementar, demasiado confusa o contradiría el código anterior.


Aquí hay un documento antiguo de 2003 n1449 . El razonamiento parece estar relacionado con las plantillas. Advertencia: puede haber errores tipográficos debido a la copia de PDF.

Primero consideremos un ejemplo de juguete:

 template  class MyAlloc {/*...*/}; template  class MyVector {/*...*/}; template  struct Vec { typedef MyVector > type; }; Vec::type p; // sample usage 

El problema fundamental con este modismo, y el hecho motivador principal para esta propuesta, es que el modismo hace que los parámetros de la plantilla aparezcan en un contexto no deducible. Es decir, no será posible llamar a la función foo a continuación sin especificar explícitamente los argumentos de la plantilla.

 template  void foo (Vec::type&); 

Entonces, la syntax es algo fea. Preferimos evitar el ::type nested ::type Preferiríamos algo como lo siguiente:

 template  using Vec = MyVector >; //defined in section 2 below Vec p; // sample usage 

Tenga en cuenta que evitamos específicamente el término “plantilla typedef” e intr oduzca la nueva syntax que implica el par “using” y “=” para ayudar a evitar confusiones: no estamos definiendo ningún tipo aquí, estamos introduciendo un sinónimo (es decir, alias) para una abstracción de un id de tipo (es decir, expresión de tipo) que involucra parámetros de plantilla. Si los parámetros de la plantilla se utilizan en contextos deducibles en la expresión de tipo, cada vez que se use el alias de la plantilla para formar una plantilla-id, se pueden deducir los valores de los parámetros de la plantilla correspondientes, más sobre esto seguirá. En cualquier caso, ahora es posible escribir funciones genéricas que operan en Vec en contexto deducible, y la syntax también se mejora. Por ejemplo, podríamos reescribir foo como:

 template  void foo (Vec&); 

Subrayamos aquí que una de las principales razones para proponer alias de plantilla fue para que la deducción de argumento y la invocación a foo(p) tengan éxito.


El documento de seguimiento n1489 explica por qué using lugar de usar typedef :

Se ha sugerido (re) usar la palabra clave typedef, como se hizo en el artículo [4], para introducir alias de plantilla:

 template typedef std::vector > Vec; 

Esa notación tiene la ventaja de utilizar una palabra clave ya conocida para introducir un alias de tipo. Sin embargo, también muestra varias desventajas entre las cuales la confusión de usar una palabra clave conocida para introducir un alias para un nombre de tipo en un contexto donde el alias no designa un tipo, sino una plantilla; Vec no es un alias para un tipo, y no debe tomarse para un typedef-name. El nombre Vec es un nombre para la familia std::vector< [bullet] , MyAllocator< [bullet] > > – donde la viñeta es un marcador de posición para un nombre de tipo. En consecuencia, no proponemos la syntax “typedef”. Por otro lado, la oración

 template using Vec = std::vector >; 

se puede leer / interpretar como: de ahora en std::vector > Vec como sinónimo de std::vector > . Con esa lectura, la nueva syntax para aliasing parece razonablemente lógica.

Creo que la distinción importante se hace aquí, alias es en lugar de tipo s. Otra cita del mismo documento:

Una statement de alias es una statement y no una definición. Una statement de alias introduce un nombre en una región declarativa como un alias para el tipo designado por el lado derecho de la statement. El núcleo de esta propuesta se refiere a los alias de nombre de tipo, pero la notación, obviamente, se puede generalizar para proporcionar deletreos alternativos de alias de espacio de nombres o nombrar un conjunto de funciones sobrecargadas (ver ✁ 2.3 para mayor discusión). [ Mi nota: esa sección discute cómo se ve esa syntax y por qué no es parte de la propuesta. ] Cabe señalar que la statement de alias de producción de gramática es aceptable en cualquier lugar en que sea aceptable una statement typedef o una definición de alias de espacio de nombres.

Resumen, para la función de using :

  • alias de plantilla (o typedefs de plantilla, el primero es preferido por nombre)
  • namespace aliases (es decir, namespace PO = boost::program_options y using PO = ... equivalente)
  • el documento dice que A typedef declaration can be viewed as a special case of non-template alias-declaration . Es un cambio estético, y se considera idéntico en este caso.
  • poner algo dentro del scope (por ejemplo, namespace std en el scope global), funciones de miembros, heredar constructores

No puede usarse para:

 int i; using r = i; // compile-error 

En lugar de hacer:

 using r = decltype(i); 

Nombrando un conjunto de sobrecargas.

 // 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);