Объявление using
Вводит имя, определённое где-то ещё, в область объявления, где появляется это объявление using.
using typename(необязательно) спецификатор_вложенного_имени неквалифицированный_идентификатор ; | (до C++17) | |
using список_деклараторов ; | (с C++17) |
typename | - | ключевое слово typename может быть использовано по необходимости для разрешения зависимых имён, когда объявление using вводит тип члена из базового класса в шаблон класса |
| спецификатор_вложенного_имени | - | последовательность имён и операторов разрешения области видимости ::, заканчивающаяся оператором разрешения области видимости. Одиночный :: ссылается на глобальное пространство имён. |
| неквалифицированный_идентификатор | - | выражение_идентификатора |
| список_деклараторов | - | список, разделённый запятыми, из одного или более деклараторов typename(необязательно) спецификатор_вложенного_имени неквалифицированный_идентификатор. Некоторые или все деклараторы могут быть следовать эллипсисом ... для обозначения расширения_пакетов |
Объяснение
Объявления 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 вводит член базового класса в определение производного класса, например, для раскрытия защищённого члена базового как публичного члена производного. В этом случае спецификатор_вложенного_имени должен называть базовый класс определённого. Если имя является именем перегруженной функции-члена базового класса, все функции-члены базового класса с этим именем вводятся. Если у производного класса уже есть член с таким же именем, списком параметров и квалификациями, член производного класса скрывает или переопределяет (не конфликтует) член, который введён из базового класса.
#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 constructorstruct 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-объявление относится к зависимому имени, оно считается именем конструктора, если спецификатор_вложенного_имени имеет конечное имя, которое такое же, как неквалифицированный_идентификатор. 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-name (дизъюнктор 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).
END_OF_DOCUMENT_MARKER| Семантика наследования конструкторов была ретроактивно изменена отчётом о дефекте по C++11. Ранее, объявление наследуемого конструктора приводило к вводу в производный класс набора синтезированных объявлений конструкторов, что вызывало избыточные копирования/перемещения аргументов, имело проблемные взаимодействия с некоторыми формами SFINAE, а в некоторых случаях было нереализуемо на основных ABIs. Более старые компиляторы могут по-прежнему реализовывать предыдущие семантики.
| (с 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++11) (DR) | Переформулировка наследуемых конструкторов | |
__cpp_variadic_using | 201611L | (C++17) |
Расширения пакетов в объявлениях using |
Отчёты о дефектах
Следующие отчёты о дефектах, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применимо к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| CWG 258 | C++98 | неконстантный член-функция производного класса может переопределять и/или скрывать константную член-функцию базового класса | переопределение и скрытие также требуют cv-квалификации быть одинаковыми |
| CWG 1738 | C++11 | не было ясно, разрешено ли явно инстанцировать или явно специализировать специализации шаблонов наследуемых конструкторов | запрещено |
| CWG 2504 | C++11 | поведение наследуемых конструкторов от виртуальных базовых классов было неясно | уточнено |
| P0136R1 | C++11 | объявление наследуемого конструктора вводит дополнительные конструкторы в производном классе | приводит к тому, что конструкторы базового класса находятся с помощью поиска по имени |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/using_declaration