En C ++ 11, la using
palabra clave cuando se usa para type alias
es idéntica a typedef
.
7.1.3.2
Un typedef-name también se puede introducir mediante una declaración de alias. El identificador que sigue a la palabra clave using se convierte en typedef-name y el atributo-specifier-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 la identificación de tipo.
Bjarne Stroustrup ofrece un ejemplo práctico:
typedef void (*PFD)(double); // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double); // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP
Antes de C ++ 11, la using
palabra clave puede llevar las funciones miembro al alcance. En C ++ 11, ahora puede hacer esto para 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 razón bastante buena detrás de la razón de no introducir una nueva palabra clave o nueva sintaxis. El estándar quiere evitar romper el código antiguo tanto como sea posible. Por eso, en la documentación de propuestas verá secciones gusta Impact on the Standard
, Design decisions
y cómo pueden afectar código antiguo. Hay situaciones en las que una propuesta parece una muy buena idea, pero podría no tener tracción porque sería demasiado difícil de implementar, demasiado confusa o contradiría el código antiguo.
Aquí hay un artículo antiguo de 2003 n1449 . La justificación parece estar relacionada con las plantillas. Advertencia: puede haber errores tipográficos debido a la copia desde PDF.
Primero consideremos un ejemplo de juguete:
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
El problema fundamental con este modismo, y el principal hecho motivador 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 <typename T> void foo (Vec<T>::type&);
Entonces, la sintaxis es algo fea. Preferiríamos evitar los anidados ::type
. Preferiríamos algo como lo siguiente:
template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage
Tenga en cuenta que evitamos específicamente el término “plantilla typedef” e introducimos la nueva sintaxis que involucra 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 una identificación de tipo (es decir, expresión de tipo) que involucra parámetros de plantilla. Si los parámetros de la plantilla se usan en contextos deducibles en la expresión de tipo, cada vez que se use el alias de la plantilla para formar un id de plantilla, se pueden deducir los valores de los parámetros de la plantilla correspondiente; más adelante se hará más sobre esto. En cualquier caso, ahora es posible escribir funciones genéricas que operan Vec<T>
en un contexto deducible, y la sintaxis también se mejora. Por ejemplo, podríamos reescribir foo como:
template <typename T> void foo (Vec<T>&);
Subrayamos aquí que una de las razones principales para proponer alias de plantilla fue para que la deducción de argumentos y el llamado a foo(p)
tener éxito.
El documento de seguimiento n1489 explica por qué en using
lugar de usar typedef
:
Se ha sugerido (re) utilizar la palabra clave typedef, como se hizo en el documento [4], para introducir alias de plantilla:
template<class T>
typedef std::vector<T, MyAllocator<T> > Vec;
Esa notación tiene la ventaja de usar una palabra clave ya conocida para introducir un alias de tipo. Sin embargo, también muestra varias desventajas, entre las cuales se encuentra 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 sintaxis "typedef". Por otro lado la oración
template<class T>
using Vec = std::vector<T, MyAllocator<T> >;
se puede leer / interpretar como: a partir de ahora, lo usaré Vec<T>
como sinónimo de std::vector<T, MyAllocator<T> >
. Con esa lectura, la nueva sintaxis para el alias 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 declaración de alias es una declaración, y no una definición. Una declaración de alias introduce un nombre en una región declarativa como un alias para el tipo designado por el lado derecho de la declaración. El núcleo de esta propuesta se refiere a los alias de nombre de tipo, pero la notación obviamente puede generalizarse para proporcionar ortografías alternativas de alias de espacio de nombres o conjunto de nombres de funciones sobrecargadas (consulte ✁ 2.3 para una discusión más detallada). [ Mi nota: esa sección discute cómo puede ser esa sintaxis y las razones por las cuales no es parte de la propuesta. ] Cabe señalar que la declaración de alias de producción gramatical es aceptable en cualquier lugar donde sea aceptable una declaración typedef o una definición de alias de espacio de nombres.
Resumen, para el papel de using
:
- alias de plantilla (o tipos de plantilla, se prefiere el primero por su nombre)
- alias de espacio de nombres (es decir,
namespace PO = boost::program_options
y using PO = ...
equivalente)
- dice el documento
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.
- traer algo al alcance (por ejemplo,
namespace std
al alcance global), funciones miembro, heredar constructores
No se puede usar para:
int i;
using r = i; // compile-error
En cambio, haga:
using r = decltype(i);
Nombrar 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);