Using-declaration
Вводит имя, определённое в другом месте, в декларативную область, где появляется эта using-декларация. Смотрите using enum и (since C++20) using namespace для других связанных деклараций.
using
typename
(необязательно)
nested-name-specifier
unqualified-id
;
|
(до C++17) | ||||||||
using
declarator-list
;
|
(начиная с C++17) | ||||||||
typename
|
- | ключевое слово typename может использоваться при необходимости для разрешения зависимых имен , когда using-объявление вводит тип-член из базового класса в шаблон класса |
| nested-name-specifier | - |
последовательность имен и операторов разрешения области видимости
::
, заканчивающаяся оператором разрешения области видимости. Одиночный
::
ссылается на глобальное пространство имен.
|
| unqualified-id | - | id-выражение |
| declarator-list | - |
разделенный запятыми список из одного или более деклараторов вида
typename
(опционально)
nested-name-specifier
unqualified-id
. Некоторые или все деклараторы могут заканчиваться многоточием
...
для указания
раскрытия пакета
|
Содержание |
Объяснение
Using-объявления могут использоваться для введения членов пространства имён в другие пространства имён и области видимости блоков, или для введения членов базового класса в определения производных классов , или для введения enumerators в пространства имён, блоки и области видимости классов (since C++20) .
|
Объявление using с более чем одним спецификатором using эквивалентно соответствующей последовательности объявлений using с одним спецификатором using. |
(since C++17) |
В пространстве имен и области видимости блока
Using-declarations вводят член другого пространства имен в текущее пространство имен или область видимости блока.
#include <iostream> #include <string> using std::string; int main() { string str = "Пример"; using std::cout; cout << str; }
См. namespace для получения подробной информации.
В определении класса
Объявление using вводит член базового класса в определение производного класса, например, чтобы сделать защищенный член базового класса открытым членом производного. В этом случае nested-name-specifier должен указывать на базовый класс определяемого класса. Если имя является именем перегруженной функции-члена базового класса, вводятся все функции-члены базового класса с этим именем. Если производный класс уже имеет член с тем же именем, списком параметров и квалификациями, член производного класса скрывает или переопределяет (не конфликтует с) членом, введенным из базового класса.
#include <iostream> struct B { virtual void f(int) { std::cout << "B::f\n"; } void g(char) { std::cout << "B::g\n"; } void h(int) { std::cout << "B::h\n"; } protected: int m; // B::m является protected typedef int value_type; }; struct D : B { using B::m; // D::m является public using B::value_type; // D::value_type является public using B::f; void f(int) override { std::cout << "D::f\n"; } // D::f(int) переопределяет B::f(int) using B::g; void g(int) { std::cout << "D::g\n"; } // обе функции g(int) и g(char) видны using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) скрывает B::h(int) }; int main() { D d; B& b = d; // b.m = 2; // Ошибка: B::m является protected d.m = 1; // protected B::m доступен как public D::m b.f(1); // вызывает производную f() d.f(1); // вызывает производную f() std::cout << "----------\n"; d.g(1); // вызывает производную g(int) d.g('a'); // вызывает базовую g(char), доступную через using B::g; std::cout << "----------\n"; b.h(1); // вызывает базовую h() d.h(1); // вызывает производную h() }
Вывод:
D::f D::f ---------- D::g B::g ---------- B::h D::h
Наследование конструкторовЕсли using-объявление ссылается на конструктор прямого базового класса определяемого класса (например, using Base :: Base ; ), все конструкторы этого базового класса (игнорируя контроль доступа) становятся видимыми для разрешения перегрузки при инициализации производного класса. Если разрешение перегрузки выбирает унаследованный конструктор, он доступен, если был бы доступен при использовании для создания объекта соответствующего базового класса: доступность using-объявления, которое его представило, игнорируется.
Если разрешение перегрузки выбирает один из унаследованных конструкторов при инициализации объекта такого производного класса, то
struct B1 { B1(int, ...) {} }; struct B2 { B2(double) {} }; int get(); struct D1 : B1 { using B1::B1; // наследует B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 инициализируется вызовом B1(2, 3, 4), // затем d.x инициализируется по умолчанию (инициализация не выполняется), // затем d.y инициализируется вызовом get() D1 e; // Ошибка: D1 не имеет конструктора по умолчанию } struct D2 : B2 { using B2::B2; // наследует B2(double) B1 b; }; D2 f(1.0); // ошибка: B1 не имеет конструктора по умолчанию struct W { W(int); }; struct X : virtual W { using W::W; // наследует W(int) X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK: инициализация Y не вызывает конструктор по умолчанию X
Если базовый подобъект класса
struct V { V() = default; V(int); }; struct Q { Q(); }; struct A : virtual V, Q { using V::V; A() = delete; }; int bar() { return 42; } struct B : A { B() : A(bar()) {} // OK }; struct C : B {}; void foo() { C c; // функция "bar" не вызывается, потому что подобъект V // не инициализируется как часть B // (подобъект V инициализируется как часть C, // потому что "c" является наиболее производным объектом) }
Если конструктор был унаследован от нескольких базовых подобъектов типа
struct A { A(int); }; struct B : A { using A::A; }; struct C1 : B { using B::B; }; struct C2 : B { using B::B; }; struct D1 : C1, C2 { using C1::C1; using C2::C2; }; D1 d1(0); // некорректно: конструктор унаследован от разных базовых подобъектов B struct V1 : virtual B { using B::B; }; struct V2 : virtual B { using B::B; }; struct D2 : V1, V2 { using V1::V1; using V2::V2; }; D2 d2(0); // корректно: существует только один подобъект B. // Это инициализирует виртуальный базовый класс B, // который инициализирует базовый класс A // затем инициализирует базовые классы V1 и V2 // как если бы конструктором по умолчанию
Как и в случае с using-объявлениями для любых других нестатических функций-членов, если унаследованный конструктор совпадает по сигнатуре с одним из конструкторов
struct B1 { B1(int); }; struct B2 { B2(int); }; struct D2 : B1, B2 { using B1::B1; using B2::B2; D2(int); // OK: D2::D2(int) скрывает как B1::B1(int), так и B2::B2(int) }; D2 d2(0); // вызывает D2::D2(int) Внутри шаблонного класса , если using-объявление ссылается на зависимое имя , оно считается именующим конструктор, если nested-name-specifier имеет терминальное имя, совпадающее с unqualified-id . template<class T> struct A : T { using T::T; // OK, наследует конструкторы T }; template<class T, class U> struct B : T, A<U> { using A<U>::A; // OK, наследует конструкторы A<U> using T::A; // не наследует конструктор T // даже если T может быть специализацией A<> }; |
(начиная с C++11) |
Введение перечислителей с областью видимостиВ дополнение к членам другого пространства имен и членам базовых классов, using-объявление также может вводить перечислители перечислений в области видимости пространства имен, блока и класса. Using-объявление также может использоваться с перечислителями без области видимости. enum class button { up, down }; struct S { using button::up; button b = up; // OK }; using button::down; constexpr button non_up = down; // OK constexpr auto get_button(bool is_up) { using button::up, button::down; return is_up ? up : down; // OK } enum unscoped { val }; using unscoped::val; // OK, though needless |
(начиная с C++20) |
Примечания
В декларативную область переносится только имя, явно указанное в using-объявлении: в частности, перечислители не переносятся, когда имя типа перечисления объявляется через using.
Объявление using не может ссылаться на пространство имён , на элемент ограниченного перечисления (до C++20) , на деструктор базового класса или на специализацию шаблона элемента для пользовательской функции преобразования.
Объявление using не может называть специализацию шаблона-члена ( template-id не допускается грамматикой):
struct B { template<class T> void f(); }; struct D : B { using B::f; // OK: объявляет шаблон // using B::f<int>; // Ошибка: объявляет специализацию шаблона void g() { f<int>(); } };
Объявление using также не может использоваться для введения имени зависимого шаблона-члена как
template-name
(дизамбигуатор
template
для
dependent names
не допускается).
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // Ошибка: дизамбигуатор не разрешен using B<Y>::f; // компилируется, но f не является именем шаблона void g() { // f<int>(0); // Ошибка: f не известно как имя шаблона, // поэтому < не начинает список аргументов шаблона f(0); // OK } };
Если using-объявление вносит оператор присваивания базового класса в производный класс, и его сигнатура совпадает с оператором копирующего или перемещающего присваивания производного класса, этот оператор скрывается неявно объявленным оператором копирующего/перемещающего присваивания производного класса. Аналогичное правило применяется к using-объявлению, которое наследует конструктор базового класса, совпадающий с конструктором копирования/перемещения производного класса (начиная с C++11) .
|
Семантика наследования конструкторов была изменена задним числом с помощью отчета о дефектах против C++11 . Ранее объявление наследуемого конструктора вызывало вставку набора синтезированных объявлений конструкторов в производный класс, что приводило к избыточным копированиям/перемещениям аргументов, имело проблемные взаимодействия с некоторыми формами SFINAE и в некоторых случаях могло быть нереализуемо на основных ABI. Старые компиляторы могут все еще реализовывать предыдущую семантику.
|
(since C++11) |
|
Pack expansions в using-объявлениях позволяют создать класс, который предоставляет перегруженные члены вариативных базовых классов без рекурсии: template<typename... Ts> struct Overloader : Ts... { using Ts::operator()...; // exposes operator() from every base }; template<typename... T> Overloader(T...) -> Overloader<T...>; // C++17 deduction guide, not needed in C++20 int main() { auto o = Overloader{ [] (auto const& a) {std::cout << a;}, [] (float f) {std::cout << std::setprecision(3) << f;} }; } |
(начиная с C++17) |
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_inheriting_constructors
|
200802L
|
(C++11) | Наследование конструкторов |
201511L
|
(C++17)
(DR11) |
Переформулировка наследования конструкторов | |
__cpp_variadic_using
|
201611L
|
(C++17) |
Расширения пакетов
в
using
-объявлениях
|
Ключевые слова
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 258 | C++98 |
неконстантная функция-член производного класса может
переопределять и/или скрывать константную функцию-член своего базового класса |
переопределение и сокрытие также требуют
одинаковых cv-квалификаций |
| CWG 1738 | C++11 |
было неясно, разрешено ли
явно инстанцировать или явно специализировать специализации шаблонов наследуемых конструкторов |
запрещено |
| CWG 2504 | C++11 |
поведение наследуемых конструкторов
от виртуальных базовых классов было неясным |
прояснено |
| P0136R1 | C++11 |
объявление наследуемого конструктора внедряет
дополнительные конструкторы в производный класс |
приводит к тому, что конструкторы базового класса
находятся при поиске по имени |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
-
-
9.9 Директива
using[namespace.udecl]
-
9.9 Директива
- Стандарт C++20 (ISO/IEC 14882:2020):
-
-
9.9 Директива
using[namespace.udecl]
-
9.9 Директива
- Стандарт C++17 (ISO/IEC 14882:2017):
-
-
10.3.3 Директива
using[namespace.udecl]
-
10.3.3 Директива
- Стандарт C++14 (ISO/IEC 14882:2014):
-
-
7.3.3 Директива
using[namespace.udecl]
-
7.3.3 Директива
- Стандарт C++11 (ISO/IEC 14882:2011):
-
-
7.3.3 Директива
using[namespace.udecl]
-
7.3.3 Директива
- Стандарт C++03 (ISO/IEC 14882:2003):
-
-
7.3.3 Директива
using[namespace.udecl]
-
7.3.3 Директива
- Стандарт C++98 (ISO/IEC 14882:1998):
-
-
7.3.3 Директива
using[namespace.udecl]
-
7.3.3 Директива