В C ++ 11 using
ключевое слово при использовании для type alias
идентично typedef
.
7.1.3.2
Typedef-name также может быть введено объявлением псевдонима. Идентификатор, следующий за ключевым словом using, становится typedef-name, а необязательный атрибут-identifier-seq, следующий за идентификатором, относится к этому typedef-name. Он имеет такую же семантику, как если бы он был введен спецификатором typedef. В частности, он не определяет новый тип и не должен появляться в идентификаторе типа.
Бьярне Страуструп дает практический пример:
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
До C ++ 11 using
ключевое слово может привести функции-члены в область видимости. В C ++ 11 теперь вы можете сделать это для конструкторов (другой пример Бьярна Страуструпа):
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)
// ...
};
Бен Войт приводит довольно вескую причину отказа от введения нового ключевого слова или нового синтаксиса. Стандарт хочет как можно больше не ломать старый код. Вот почему в предложении документов вы увидите разделы нравится Impact on the Standard
, Design decisions
и как они могут повлиять на старый код. Бывают ситуации, когда предложение кажется действительно хорошей идеей, но может не иметь тяги, потому что его будет слишком сложно реализовать, слишком запутанно или будет противоречить старому коду.
Вот старая статья 2003 года №1449 . Похоже, обоснование связано с шаблонами. Предупреждение: возможны опечатки из-за копирования из PDF.
Сначала давайте рассмотрим игрушечный пример:
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
Фундаментальная проблема с этой идиомой и основной мотивирующий факт для этого предложения состоит в том, что идиома заставляет параметры шаблона появляться в не выводимом контексте. То есть ниже будет невозможно вызвать функцию foo без явного указания аргументов шаблона.
template <typename T> void foo (Vec<T>::type&);
Итак, синтаксис несколько уродлив. Мы бы предпочли избегать вложенных. ::type
Мы бы предпочли что-то вроде следующего:
template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage
Обратите внимание, что мы специально избегаем термина «typedef template» и вводим новый синтаксис, включающий пару «using» и «=», чтобы избежать путаницы: мы не определяем здесь какие-либо типы, мы вводим синоним (то есть псевдоним) для абстракция идентификатора типа (т.е. выражения типа), включающего параметры шаблона. Если параметры шаблона используются в выводимых контекстах в выражении типа, то всякий раз, когда псевдоним шаблона используется для формирования идентификатора шаблона, можно определить значения соответствующих параметров шаблона - подробнее об этом будет сказано ниже. В любом случае, теперь возможно написать универсальные функции, которые работают Vec<T>
в выводимом контексте, и синтаксис также улучшен. Например, мы можем переписать foo как:
template <typename T> void foo (Vec<T>&);
Здесь мы подчеркиваем, что одной из основных причин для предложения псевдонимов шаблонов было то, что вывод аргумента и призыв к foo(p)
успеху будут успешными.
В последующей статье n1489 объясняется, почему using
вместо использования typedef
:
Было предложено (повторно) использовать ключевое слово typedef - как это сделано в статье [4] - для введения псевдонимов шаблона:
template<class T>
typedef std::vector<T, MyAllocator<T> > Vec;
Преимущество этой нотации заключается в использовании уже известного ключевого слова для введения псевдонима типа. Однако он также отображает несколько недостатков, среди которых путаница в использовании ключевого слова, известного как введение псевдонима имени типа в контексте, где псевдоним не обозначает тип, но шаблон; Vec
не является псевдонимом для типа и не должен восприниматься как typedef-name. Имя Vec
- это имя для семьи std::vector< [bullet] , MyAllocator< [bullet] > >
- где маркер является заполнителем для имени типа. Следовательно, мы не предлагаем синтаксис «typedef». С другой стороны предложение
template<class T>
using Vec = std::vector<T, MyAllocator<T> >;
можно читать / интерпретировать как: теперь я буду использовать Vec<T>
как синоним для std::vector<T, MyAllocator<T> >
. С этим чтением новый синтаксис для псевдонимов кажется достаточно логичным.
Я думаю, что здесь проводится важное различие, псевдоним es вместо типа s. Еще одна цитата из того же документа:
Псевдоним-декларация - это декларация, а не определение. Объявление псевдонима вводит имя в декларативную область как псевдоним для типа, указанного в правой части объявления. Суть этого предложения касается псевдонимов имен типов, но обозначения, очевидно, могут быть обобщены для обеспечения альтернативного написания псевдонимов пространства имен или набора имен перегруженных функций (см. П. 2.3 для дальнейшего обсуждения). [ Мое примечание: в этом разделе обсуждается, как может выглядеть этот синтаксис, и причины, по которым он не является частью предложения. ] Можно отметить, что объявление псевдонима производства грамматики приемлемо везде, где допустимо объявление typedef или определение пространства имен псевдонима.
Резюме, на роль using
:
- Псевдонимы шаблона (или определение типа шаблона, первое предпочтительнее по имени)
- псевдонимы пространства имен (т. е.
namespace PO = boost::program_options
и using PO = ...
эквивалентные)
- в документе говорится
A typedef declaration can be viewed as a special case of non-template alias-declaration
. Это эстетическое изменение, и в этом случае оно считается идентичным.
- внесение чего-либо в область видимости (например,
namespace std
в глобальную область видимости), функции-члены, наследующие конструкторы
Его нельзя использовать для:
int i;
using r = i; // compile-error
Вместо этого сделайте:
using r = decltype(i);
Называя набор перегрузок.
// 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);