Объявление using
Вводит имя, определённое в другом месте, в область декларативной видимости, где появляется это объявление using. См. using enum и (начиная с 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 | - | выражение-идентификатор |
| declarator-list | - | разделённый запятыми список из одного или нескольких объявителей вида typename(необязательно) nested-name-specifier unqualified-id. За некоторыми или всеми объявителями может следовать многоточие ..., указывающее на раскрытие пакета |
Пояснение
Объявления using можно использовать для введения членов пространства имён в другие пространства имён и области видимости блока, а также для введения членов базового класса в определения производных классов или для введения перечислителей в пространства имён и области видимости блока и класса(начиная с C++20).
| Объявление using с несколькими using-деклараторами эквивалентно соответствующей последовательности объявлений using, каждое из которых содержит один using-декларатор. | (начиная с C++17) |
В пространстве имён и области видимости блока
Объявления using вводят член другого пространства имён в текущее пространство имён или область видимости блока.
#include <iostream>
#include <string>
using std::string;
int main()
{
string str = "Example";
using std::cout;
cout << str;
}
Подробнее см. пространство имён.
В определении класса
Объявление 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 is protected
typedef int value_type;
};
struct D : B
{
using B::m; // D::m is public
using B::value_type; // D::value_type is public
using B::f;
void f(int) override { std::cout << "D::f\n"; } // D::f(int) overrides B::f(int)
using B::g;
void g(int) { std::cout << "D::g\n"; } // both g(int) and g(char) are visible
using B::h;
void h(int) { std::cout << "D::h\n"; } // D::h(int) hides B::h(int)
};
int main()
{
D d;
B& b = d;
// b.m = 2; // Error: B::m is protected
d.m = 1; // protected B::m is accessible as public D::m
b.f(1); // calls derived f()
d.f(1); // calls derived f()
std::cout << "----------\n";
d.g(1); // calls derived g(int)
d.g('a'); // calls base g(char), exposed via using B::g;
std::cout << "----------\n";
b.h(1); // calls base h()
d.h(1); // calls derived h()
}
Вывод:
D::f D::f ---------- D::g B::g ---------- B::h D::h
Наследование конструкторовЕсли объявление using ссылается на конструктор прямого базового класса определяемого класса (например, Если при разрешении перегрузки выбран унаследованный конструктор, к нему есть доступ, если бы к нему был доступ при создании объекта соответствующего базового класса: доступность объявления using, которое его ввело, не учитывается. Если при инициализации объекта такого производного класса при разрешении перегрузки выбран один из унаследованных конструкторов, подобъект struct B1 { B1(int, ...) {} };
struct B2 { B2(double) {} };
int get();
struct D1 : B1
{
using B1::B1; // inherits B1(int, ...)
int x;
int y = get();
};
void test()
{
D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4),
// then d.x is default-initialized (no initialization is performed),
// then d.y is initialized by calling get()
D1 e; // Error: D1 has no default constructor
}
struct D2 : B2
{
using B2::B2; // inherits B2(double)
B1 b;
};
D2 f(1.0); // error: B1 has no default constructor
struct W { W(int); };
struct X : virtual W
{
using W::W; // inherits W(int)
X() = delete;
};
struct Y : X
{
using X::X;
};
struct Z : Y, virtual W
{
using Y::Y;
};
Z z(0); // OK: initialization of Y does not invoke default constructor of 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” is not invoked, because the V subobject
// is not initialized as part of B
// (the V subobject is initialized as part of C,
// because “c” is the most derived object)
}
Если конструктор унаследован от нескольких подобъектов базового класса типа 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); // ill-formed: constructor inherited from different B base subobjects
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); // OK: there is only one B subobject.
// This initializes the virtual B base class,
// which initializes the A base class
// then initializes the V1 and V2 base classes
// as if by a defaulted default constructor
Как и в случае объявлений 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) hides both B1::B1(int) and B2::B2(int)
};
D2 d2(0); // calls D2::D2(int)
Внутри шаблонного класса, если объявление using ссылается на зависимое имя, оно считается обозначающим конструктор, если конечное имя в nested-name-specifier совпадает с unqualified-id. template<class T>
struct A : T
{
using T::T; // OK, inherits constructors of T
};
template<class T, class U>
struct B : T, A<U>
{
using A<U>::A; // OK, inherits constructors of A<U>
using T::A; // does not inherit constructor of T
// even though T may be a specialization of 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: names a template
// using B::f<int>; // Error: names a template specialization
void g() { f<int>(); }
};
Объявление using также нельзя использовать для введения имени зависимого шаблона-члена в качестве имени шаблона (дизамбигуатор template для зависимых имён не допускается).
template<class X>
struct B
{
template<class T>
void f(T);
};
template<class Y>
struct D : B<Y>
{
// using B<Y>::template f; // Error: disambiguator not allowed
using B<Y>::f; // compiles, but f is not a template-name
void g()
{
// f<int>(0); // Error: f is not known to be a template name,
// so < does not start a template argument list
f(0); // OK
}
};
Если объявление using вводит оператор присваивания базового класса в производный класс, и его сигнатура совпадает с оператором присваивания копированием или перемещением производного класса, этот оператор скрывается неявно объявленным оператором присваивания копированием/перемещением производного класса. То же относится к объявлению using, наследующему конструктор базового класса, сигнатура которого совпадает с конструктором копирования/перемещения производного класса(начиная с C++11).
| Семантика наследования конструкторов была изменена задним числом в соответствии с отчётом об ошибке для C++11. Ранее объявление наследуемого конструктора приводило к внедрению в производный класс набора синтезированных объявлений конструкторов, что вызывало избыточное копирование/перемещение аргументов, проблемное взаимодействие с некоторыми формами SFINAE, а в некоторых случаях было невозможно реализовать в основных ABI. Старые компиляторы всё ещё могут реализовывать прежнюю семантику.
| (начиная с C++11) |
| Раскрытия пакетов в объявлениях 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 | Применяется к | Поведение согласно опубликованной редакции | Корректное поведение |
|---|---|---|---|
| CWG 258 | C++98 | нестатическая функция-член производного класса, не являющаяся const, может переопределять и/или скрывать const-функцию-член базового класса | для переопределения и сокрытия также требуется совпадение cv-квалификаторов |
| CWG 1738 | C++11 | не было ясно, разрешено ли явно инстанцировать или явно специализировать специализации шаблонов наследуемых конструкторов | запрещено |
| CWG 2504 | C++11 | поведение при наследовании конструкторов от виртуальных базовых классов было неясным | уточнено |
| P0136R1 | C++11 | объявление наследуемого конструктора внедряет дополнительные конструкторы в производный класс | конструкторы базового класса становятся доступными при поиске имён |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
- 9.9 Объявление
using[namespace.udecl] - Стандарт C++20 (ISO/IEC 14882:2020):
- 9.9 Объявление
using[namespace.udecl] - Стандарт C++17 (ISO/IEC 14882:2017):
- 10.3.3 Объявление
using[namespace.udecl] - Стандарт C++14 (ISO/IEC 14882:2014):
- 7.3.3 Объявление
using[namespace.udecl] - Стандарт C++11 (ISO/IEC 14882:2011):
- 7.3.3 Объявление
using[namespace.udecl] - Стандарт C++03 (ISO/IEC 14882:2003):
- 7.3.3 Объявление
using[namespace.udecl] - Стандарт C++98 (ISO/IEC 14882:1998):
- 7.3.3 Объявление
using[namespace.udecl]
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/using_declaration