Using-declaration
이 using 선언이 나타나는 선언 영역에 다른 곳에서 정의된 이름을 도입합니다. 관련된 다른 선언에 대해서는 using enum 및 (C++20부터) using namespace 를 참조하십시오.
using
typename
(선택적)
nested-name-specifier
unqualified-id
;
|
(C++17 이전) | ||||||||
using
declarator-list
;
|
(C++17 이후) | ||||||||
typename
|
- | 키워드 typename 은 using 선언이 클래스 템플릿의 기본 클래스로부터 멤버 타입을 도입할 때 종속 이름(dependent names) 을 해결하기 위해 필요한 경우 사용될 수 있음 |
| nested-name-specifier | - |
이름과 범위 확인 연산자
::
의 시퀀스로, 범위 확인 연산자로 끝남. 단일
::
는 전역 네임스페이스를 참조함
|
| unqualified-id | - | 식별자 표현(id-expression) |
| declarator-list | - |
typename
(선택사항)
nested-name-specifier
unqualified-id
의 하나 이상의 선언자(declarators)를 쉼표로 구분한 목록. 일부 또는 모든 선언자는
팩 확장(pack expansion)
을 나타내기 위해 말줄임표
...
가 뒤따를 수 있음
|
목차 |
설명
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 = "Example"; using std::cout; cout << str; }
자세한 내용은 namespace 를 참조하십시오.
클래스 정의 내에서
using 선언은 기저 클래스의 멤버를 파생 클래스 정의 내로 도입합니다. 예를 들어 기저의 protected 멤버를 파생의 public 멤버로 노출하는 경우가 있습니다. 이 경우 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-declaration 이 정의 중인 클래스의 직접 기반 클래스 생성자를 참조하는 경우(예: using Base :: Base ; ), 해당 기반 클래스의 모든 생성자들(멤버 접근 제어는 무시)은 파생 클래스를 초기화할 때 오버로드 해결을 위해 visible 상태가 됩니다. 오버로드 해결이 상속된 생성자를 선택하면, 해당하는 기본 클래스의 객체를 생성하는 데 사용될 때 접근 가능한 경우 접근 가능합니다: 이를 도입한 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()) {} // 정상 }; 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 선언이 종속 이름 을 참조하는 경우, 중첩 이름 지정자 의 최종 이름이 한정되지 않은 식별자 와 동일할 때 생성자를 지칭하는 것으로 간주됩니다. 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
디스앰비귀에이터는 허용되지 않습니다).
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // 오류: 분해자(disambiguator)가 허용되지 않음 using B<Y>::f; // 컴파일되지만 f는 템플릿 이름이 아님 void g() { // f<int>(0); // 오류: f가 템플릿 이름으로 알려져 있지 않아 // <가 템플릿 인자 목록을 시작하지 않음 f(0); // 정상 } };
using 선언이 기본 클래스의 대입 연산자를 파생 클래스로 가져올 때, 그 시그니처가 우연히 파생 클래스의 복사 대입 또는 이동 대입 연산자와 일치하는 경우, 해당 연산자는 파생 클래스의 암시적으로 선언된 복사/이동 대입 연산자에 의해 가려집니다. 기본 클래스 생성자를 상속하는 using 선언이 우연히 파생 클래스의 복사/이동 생성자와 일치하는 경우에도 동일하게 적용됩니다 (C++11부터) .
|
상속 생성자의 의미론(semantics)은 C++11에 대한 결함 보고서 에 의해 소급되어 변경되었습니다. 이전에는 상속 생성자 선언이 파생 클래스에 합성된 생성자 선언 집합을 주입하도록 했으며, 이는 중복된 인자 복사/이동을 초래하고, 일부 형태의 SFINAE와 문제있는 상호작용을 일으키며, 일부 경우 주요 ABI에서 구현 불가능할 수 있었습니다. 이전 컴파일러들은 여전히 이전 의미론을 구현할 수 있습니다.
|
(since C++11) |
|
Pack expansions in using-declarations make it possible to form a class that exposes overloaded members of variadic bases without recursion: 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) |
패킷 확장
in
using
-선언문
|
키워드
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| 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