[include(틀:관련 문서, top1=C++, top2=이름공간)] [include(틀:상위 문서, top1=C++/문법)] [include(틀:프로그래밍 언어 문법)] [목차] == 개요 == ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua ''{{{ 식별자}}}''}}} {{{{}}} ... {{{}}}} }}}|| '''이름 공간 (Namespace)''' 이름공간, 또는 네임스페이스는 식별자 사이의 이름 충돌을 막기 위한 장치이다. 이름공간은 각각 분리된 프로그램처럼 존재하며 다른 이름공간끼리는 별도의 지시자없이는 참조할 수 없다 [* 이를 코드 범위(Scope)가 다르다고 한다]. [[C언어]]에서 가장 큰 문제 중 하나가 프로젝트가 커질수록 식별자의 명칭이 겹칠 위험이 커지는 것이였다. 프로젝트가 일정 규모 이상 커지거나, 외부 라이브러리를 사용하는 경우 객체[* 함수, 상수, 클래스]들의 이름 중복 현상이 발생할 가능성이 커진다. 예를 들어 비슷한 용도의 반복적인 기저 코드 작성(Boilerplate)이 프로젝트에서 반복되다 보면 단어란 단어는 다 소모하고 비슷한 유틸리티 함수가 늘어난다. 이때 다른 라이브러리 사이에 같은 이름의 객체가 있으면 컴파일러는 이를 구분할 수 없다. 차라리 모호하다고 컴파일 오류를 내거나 런타임 오류라도 나면 다행이지만, 그렇지 않고 정상작동하는 것처럼 보이는 코드가 되면 예측할 수 없는 동작을 할 것이다. [[C++]]에서 도입된 이름 공간은 큰 규모의 프로그램 개발에서 객체의 명명 문제를 대부분 해결해준다 [* 많은 [[객체 지향 프로그래밍|객체 지향 언어]]가 그렇듯 C++에서도 두 클래스의 이름이 같더라도 속한 이름공간이 다르면 공존할 수 있다]. {{{#!folding {{{#!syntax cpp import ; import ; namespace Namu { class MyPrinter { public: void Execute() const { std::println(myCaption); } private: std::string myCaption = "나무위키"; }; } namespace Wiki { class MyPrinter { public: void Execute() const { std::println(myCaption); } private: std::string myCaption = "NamuWiki"; }; } int main() { Namu::MyPrinter printer1; Wiki::MyPrinter printer2; printer1.Execute(); printer2.Execute(); return 0; } }}} }}} 예제 코드의 콘솔에서의 실행 결과는 다음과 같다. ||{{{나무위키}}}|| ||{{{Namu Wiki}}}|| 첫번째 줄에는 '나무위키'가 출력되고 두번째 줄에는 'NamuWiki'가 출력된다. == Global Namespace == {{{#!folding [ 펼치기 / 접기 ] {{{#!syntax cpp import ; import ; class Namu { public: void Execute() const { std::println(myCaption); } private: std::string myCaption = "Namu"; }; class Wiki { public: void Execute() const { std::println(myCaption); } private: std::string myCaption = "Wiki"; }; int main(void) { Namu printer1; Wiki printer2; // 또는 ::Wiki printer2; printer1.Execute(); printer2.Execute(); return 0; } }}} }}}'''전역적 이름공간 (Global Namespace)''' 이름공간이 명시 되어있지 않을 때는 전역적 이름공간을 사용하게 된다. [[C언어]]도 기본적으로 전역적 이름공간을 사용한다. 이름 공간의 이름을 적지 않고 {{{::}}}만을 사용하면 전역 이름공간을 사용하겠다고 지시할 수 있다. 그러나 전역 이름공간의 사용은 일반적으로 권장되지 않는다. 이름공간의 목적은 다른 저작자의 코드를 구분하거나, 다른 역할을 하지만 혹여나 중복될 수 있는 이름을 구별하기 위해 만들어 진 것인데 전역 이름공간은 이를 무력화시킨다. 또한 전통적인 헤더 파일 구조에서 전역 이름공간을 사용하면 그 헤더를 삽입하는 소스 코드도 영향을 받기 때문이다. 만약 C++20의 모듈이라도 {{{export}}}한 객체가 전역적 이름공간에 있다면 피할 수 없는 문제다. 그렇기에 소스 구현부 또는 스코프 안에서만 사용하는 것이 좋다. == using == {{{#!folding {{{#!syntax cpp namespace NamuWiki { struct Squirrel { float x, y, z; }; Squirrel MakeSquirrel() noexcept { return Squirrel{}; } Squirrel MakeSquirrel(float x, float y, float z) noexcept { return Squirrel{ x, y, z }; } } int main() { // (1) // 일반적인 경우 이름공간을 명시해야 한다 NamuWiki::Squirrel squirrel0{}; // auto를 통해 이름공간을 일일히 기입하는 수고를 덜 수 있다 auto squirrel1 = NamuWiki::MakeSquirrel(30, 20, 40); // (2) using NamuWiki::Squirrel; // 이제 NamuWiki::Squirrel을 그대로 사용할 수 있다 const Squirrel squirrel3; // (3) using NamuWiki::MakeSquirrel; // 이제 함수 NamuWiki::MakeSquirrel의 모든 오버로딩을 그대로 사용할 수 있다 NamuWiki::Squirrel squirrel4 = MakeSquirrel(); NamuWiki::Squirrel squirrel5 = MakeSquirrel(50, 600, 130); // (4) // 이제 이름공간 NamuWiki 안의 멤버를 모두 그대로 사용할 수 있다. using namespace NamuWiki; Squirrel squirrel6 = MakeSquirrel(40, 100, 200); } }}} }}} === 이름공간 멤버 차용 === ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#Aquamarine,#Aqua {{{Namespace}}}}}}{{{::}}}{{{#F97575,#IndianRed {{{Function}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#Aquamarine,#Aqua {{{Namespace}}}}}}{{{::}}}{{{#FFA3D2,#Violet {{{Variable}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{typename }}}'''}}}{{{#Aquamarine,#Aqua {{{Namespace}}}}}}{{{::}}}{{{#LightSeaGreen,#DarkTurquoise {{{Type}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#Aquamarine,#Aqua {{{Namespace}}}}}}{{{::}}}{{{#LightSeaGreen,#DarkTurquoise {{{Type}}}}}}{{{;}}} }}}|| '''{{{using}}} 선언문 (Using Declarations)''' {{{using}}}을 이용하면 이름공간의 멤버에 접근할 때 축약된 표현을 사용할 수 있다. 이 기능은 이름공간을 이용하면서 이름공간의 식별자를 일일이 표기하면서 생기는 불편함을 줄여준다. 주목할 점은 ''선언문''이라는 것인데, 이름공간의 같은 멤버에 계속 {{{using}}} 선언문을 써도 문제가 없다. 명시한 이름공간의 멤버를 현재 이름공간으로 끌어오는 논리라 중복 정의의 문제가 없다. 예를 들어 {{{using [이름공간 식별자]::[멤버 이름];}}}이라고 적은 경우를 생각해보자. {{{using [이름공간 Ns]::[클래스 Type];}}}가 선언된 경우에는 클래스 {{{Type}}}에 대한 인스턴스를 선언할 때에 {{{Ns::Type [인스턴스 이름];}}}이라고 적지 않고 {{{Type [인스턴스 이름]}}}이라고 적을 수 있다. {{{using [이름공간 Ns]::[함수 Function];}}}이 선언된 경우에는 해당 함수를 호출할 때에 {{{Ns::Function(...);}}}이 아니라 바로 {{{Function(...);}}}으로 호출할 수 있다. === 이름공간 차용 === ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier1}}}}}} {{{{}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier2}}}}}} {{{{}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Namespace}}}}}}{{{;}}} {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Namespace}}}}}}{{{;}}} }}}|| '''{{{using}}} 지시자 (Using Directive)''' {{{using}}} 지시자 기능을 사용하면 이름공간 안의 모든 멤버를 현재 이름공간으로 가져올 수 있다. 이러면 이름공간의 식별자를 명시하지 않고도 그 이름공간 안에 있는 모든 멤버에 바로 접근할 수 있다. [[C++]]에서 가장 많이 사용되는 {{{using namespace std;}}} 구문은 C++의 표준 라이브러리에 대한 이름공간인 {{{std}}}를 명시하지 않고 항상 {{{std}}}안의 멤버를 사용하겠다는 뜻이다. 예를 들어 {{{std::cout}}} 대신에 {{{cout}}}라고 축약해서 명시할 수 있다. 예를 들어 {{{using namespace [네임스페이스 Ns];}}}가 선언된 경우에는 이미 {{{using [네임스페이스 Ns]::[클래스 Type];}}}나 {{{using [네임스페이스 Ns]::[함수 Function];}}}를 선언한 것과 같아서 {{{Ns::Type [인스턴스 이름];}}} 대신에 바로 {{{Type [인스턴스 이름]}}}이라고 인스턴스를 생성할 수 있고, {{{Ns::Function(...);}}}이 아니라 {{{Function(...);}}}를 바로 호출할 수 있다. 그런데 헤더 파일의 전역 이름공간에는 {{{using}}}을 사용하지 않는 것이 좋다. 이는 [[C++]]의 {{{#include}}}문이 헤더 파일의 내용을 그대로 복사 & 붙여넣기를 하는 식으로 작동하기 때문이다. {{{using}}} 문을 사용하는 헤더를 포함하면 이를 삽입한 모든 파일에 {{{using}}} 문이 강제적으로 적용되어 버리고, {{{using}}}을 취소할 방법이 있는 것도 아니기에 남의 코드를 직접 수정해야 하는 [[영 좋지 않은]] 상황이 만들어진다. == 중첩 이름공간 == ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier1}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier2}}}}}} {{{{}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier3}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier4}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier5}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{IdentifierN}}}}}}{{{...}}} {{{{}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier1}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier2}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{using }}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier3}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier4}}}}}}{{{::멤버;}}} }}}|| '''중첩 이름공간 (Nested Namespace)'''^^{{{-1 {{{#008000,#a3ff84 C++17}}} }}}^^ C++17부터 이름공간 선언을 {{{::}}}와 중첩해서 할 수 있게 되었다. [[C++]] 입문 단계를 넘어가면 편의성을 체감할 수 있는 기능이다. C++17 이전에는 이름공간 쓰기가 매우 불편했는데 간단한 문법 개선으로 편의성을 증대시켰다. == inline == ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{inline}}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{ namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier1}}}}}} {{{{}}} {{{// C++11}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier2}}}}}} {{{{}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{inline}}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{ namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier3}}}}}} {{{{}}} {{{// C++11}}} ... {{{}}}} {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier4}}}}}}{{{::}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{inline }}}'''}}}{{{#Aquamarine,#Aqua {{{Identifier5}}}}}} {{{{}}} {{{// C++20}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{inline}}}'''}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{ namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier6}}}}}}{{{::}}}''{{{#DodgerBlue,#CornFlowerBlue '''{{{inline}}}'''}}}''{{{#Aquamarine,#Aqua {{{ Identifier7}}}}}}{{{::}}}''{{{#DodgerBlue,#CornFlowerBlue '''{{{inline}}}'''}}}''{{{#Aquamarine,#Aqua {{{ IdentifierN}}}}}}{{{...}}} {{{{}}} {{{// C++20}}} ... {{{}}}} }}}|| '''인라인 이름공간 (Inline Namespace)'''^^{{{-1 {{{#008000,#a3ff84 C++11}}} }}}^^, '''중첩된 인라인 이름공간 (Nested Inline Namespace)'''^^{{{-1 {{{#008000,#a3ff84 C++20}}} }}}^^ 인라인 이름공간을 사용하면 선언한 이름공간의 내용물을 상위 이름공간에서 바로 사용할 수 있다. 사실 인라인 이름공간의 원리는 상위 이름공간에 내용을 먼저 집어넣고, 인라인 이름공간 안에서 {{{using}}}을 쓰는 것에 가깝다. 인라인 이름공간을 쓰면 이전에 선언된 이름공간의 식별자를 덮어쓴다. 이를 이용해 언어 내부에서 버전 관리를 할 수 있다. [[Microsoft]]의 [[DirectX]]와 [[Windows Runtime|WinUI]]에서 이런 식으로 기능을 업그레이드하고 있다. {{{#!folding {{{#!syntax cpp // (1) // 중첩 이름공간 선언 // 해당 클래스 선언들은 이름공간 NamuWiki::Documents::ProgrammingLanguage에도 공존하게 된다 namespace NamuWiki::Documents::ProgrammingLanguage::inline FromEnhaWiki { class Assemybly; class C; class Erlang; class Cpp; class Java class Python; class JavaScript; class Csharp; class Go; class Swift; class Rust; } // (2) // 식별자 덮어씌우기 namespace House::LoungeRoom::Furnitures { inline namespace Y2019 { class Television { public: std::string hwVender = "Samsung"; }; class Fan { public: std::string hwVender = "Dyson"; }; class Couch; class Chair; class LargeTable; } inline namespace Y2021 { class Television { public: std::string hwVender = "Samsung"; }; class Fan // Y2019::Fan 클래스를 덮어쓴다. { public: std::string hwVender = "LG"; }; class AirConditioner; class MiniBar; } inline namespace Y2023 { class Television // Y2021::Television 클래스를 덮어쓴다. { public: std::string hwVender = "LG"; }; } } }}} }}} == 이름공간 별칭 == ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier1}}}}}} {{{{}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier2}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier3}}}}}} {{{{}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier4}}}}}}{{{::}}}{{{#DodgerBlue,#CornFlowerBlue '''{{{inline}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier5}}}}}} {{{{}}} ... {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ Identifier6}}}}}} {{{{}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ NamespaceAlias2}}}}}}{{{ = }}}{{{#Aquamarine,#Aqua {{{Identifier2}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ NamespaceAlias4}}}}}}{{{ = }}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier4}}}}}}{{{;}}} {{{}}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ NamespaceAlias1}}}}}}{{{ = }}}{{{#Aquamarine,#Aqua {{{Identifier1}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ NamespaceAlias3}}}}}}{{{ = }}}{{{#Aquamarine,#Aqua {{{Identifier2}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier3}}}}}}{{{;}}} {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}}{{{#Aquamarine,#Aqua {{{ NamespaceAlias5}}}}}}{{{ = }}}{{{#Aquamarine,#Aqua {{{Identifier4}}}}}}{{{::}}}{{{#Aquamarine,#Aqua {{{Identifier5}}}}}}{{{;}}} }}}|| '''이름공간 별칭 (Namespace Aliases)''' == 익명 이름공간 == ||{{{#!wiki style="font-size:1.06em" {{{#DodgerBlue,#CornFlowerBlue '''{{{namespace}}}'''}}} {{{{}}} ... {{{}}}} }}}|| '''이름없는 이름공간 (Unnamed Namespace)'''^^{{{-1 {{{#008000,#a3ff84 C++17}}} }}}^^ 이름공간을 선언할 때 식별자를 지정하지 않으면 '''이름없는 이름공간''' 또는 '''익명 이름공간'''이 된다. 이 경우 익명 이름공간이 선언된 범위에서 {{{using namespace (익명 이름공간);}}}을 사용하는 것과 같은 동작을 수행한다. 이 기능의 진정한 의의는 {{{static}}}을 대체제로써 내부 연결을 C++의 모든 객체에 적용하는데에 있다. 자세한 내용은 [[../#s-6|언어 연결성]] 단락을 참고하자. {{{#!folding {{{#!syntax cpp namespace CPUVenders { class Intel; class AMD; class [[deprecated]] VIA; } namespace { class Nvidia; // CPUVenders::AMD, CPUVenders::Intel과 ::AMD, ::Intel은 다른 존재다. // 같은 객체로 취급하려면 using CPUVenders::...를 사용해야만 한다. class AMD; class Intel; } namespace HardwareVenders { namespace { GigaByte, Asus, } // using (익명)::GigaByte; // using (익명)::Asus; namespace CPUVenders = ::CPUVenders; namespace GPUVenders { using ::Nvidia; using ::AMD; using ::Intel; } } }}} }}} [각주][include(틀:문서 가져옴, title=C++/문법, version=374, paragraph=6)] [[분류:프로그래밍 언어 문법]]