Яка логіка, що стоїть за ключовим словом "використання" в C ++?


145

Яка логіка, що стоїть за ключовим словом "використання" в C ++?

Він використовується в різних ситуаціях, і я намагаюся знайти, чи всі вони мають щось спільне, і є причина, чому ключове слово "using" використовується як таке.

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

53
Стандартний комітет ненавидить введення нових ключових слів у граматику C ++.
Інтернети зроблені з Catz

4
@tehinternetsismadeofcatz Якщо це справді логіка, вибачте, будь ласка, зараз я піду і вбию себе.
user3111311

62
@ user3111311: Ви визнаєте наслідки введення нових зарезервованих слів, правда? Це означає, що весь існуючий код, який використовував їх як імена ідентифікаторів, раптом не вдається зібрати. Це ДУЖЕ ЧАС. Наприклад, є багато коду С, який не можна скласти як C ++, оскільки він містить такі речі int class;. Ще гірше було б, якби код C ++ раптом перестав бути дійсним C ++.
Бен Войгт

7
@BenVoigt: Те, що використання коду C int class;не збирається як C ++, не зовсім погана річ. Він може бути використаний для гарантії того, що код C буде складений як C. Занадто просто забувати, що C і C ++ - це дві різні мови - і, практично кажучи, існує код, який є дійсним C та дійсним C ++, але з різною семантикою.
Кіт Томпсон

1
У цьому плані usingне гірше (або краще), ніж static. ІМХО дуже важливим є питання про те, що не вводити нові ключові слова, оскільки це пояснюється міжмережами, зробленими катцом та Бен Фогтом.
Кассіо Нері

Відповіді:


114

У C ++ 11 usingключове слово при використанні для type aliasнього ідентичне typedef.

7.1.3.2

Ім'я typedef також може бути введено декларацією псевдоніму. Ідентифікатор, що відповідає ключовому слову, що використовується, стає іменем typedef, а необов'язковий атрибут-specier-seq, що слідує за ідентифікатором, відноситься до цього імені typedef. Він має таку саму семантику, як якщо б вона була введена специфікатором typedef. Зокрема, він не визначає новий тип і не повинен відображатися в ідентифікаторі типу.

Bjarne Stroustrup - практичний приклад:

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 тепер ви можете це зробити для конструкторів (інший приклад 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 є досить вагомою причиною обґрунтування невведення нового ключового слова чи нового синтаксису. Стандарт хоче максимально уникнути порушення старого коду. Ось чому в реченні документів ви побачите розділи подобається Impact on the Standard, Design decisionsі як вони можуть вплинути на старий код. Бувають ситуації, коли пропозиція здається дійсно хорошою ідеєю, але може не мати тяги, оскільки це буде занадто важко втілити, занадто заплутане або суперечить старому коду.


Ось старий папір від 2003 року n1449 . Обґрунтування, схоже, пов'язане з шаблонами. Попередження: можливі помилкові помилки через копіювання з 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. Ім'я 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 або простір імен-alias-definition є прийнятною.

Підсумок, на роль using:

  • псевдоніми шаблонів (або шаблони typedefs, колишній бажано називати іменем)
  • Псевдоніми просторів імен (тобто, 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);

2
@ user3111311 Яке ще ключове слово ви мали на увазі? "авто"? "зареєструватися"?
Реймонд Чен

2
using P = [](double)->void;є, AFAIK, недійсний C ++ 11. Однак це: using P = auto(double)->void;і створює тип функції (такий, що P*є покажчиком функції).
dyp

2
Його звуть Bjarne Stroustrup;) (зверніть увагу на другий r у Stroustrup)
dyp

1
@RaymondChen: насправді registerце не звучить так погано, є в:register X as Y
MFH

1
На жаль, registerпочинається оголошення змінної, тому це вже має сенс. Оголосіть змінну регістра під назвою Y типу X.
Raymond Chen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.