문서 보기문서 편집수정 내역 C++ (r1 버전으로 되돌리기) [include(틀:프로그래밍 언어)] [include(틀:TIOBE/C++)] [[파일:C++ 로고.svg|width=240&align=right]] [목차] == 개요 == {{{#!syntax cpp import std; // 또는 import ; int main() { std::print("Hello, world!"); } }}}[* C++23의 모듈을 사용한 출력 예시] {{{#!folding C++20까지의 Hello, World! ## 아래 예제 부분을 수정 전 https://namu.wiki/thread/AStimulatingAndRomanticLaugh 토론을 반드시 확인하고 수정하시기 바랍니다. ## http://www.stroustrup.com/hello_world.c {{{#!syntax cpp #include int main () { std::cout << "Hello, world!"; } }}}[* C++17 까지의 헤더를 사용한 출력 예시] [*std 예제 코드나 대회 제출용 코드를 보면, 헤더 삽입 부분 아래에 '''using namespace std;'''를 입력하여 '''std''' 네임스페이스를 생략하는 경우가 있는데, 실무에서는 사용하지 말 것을 강력히 권장한다. 다른 네임스페이스에 같은 이름의 함수나 변수가 존재할 수 있으며, 이러한 경우 using문으로 전역 네임스페이스에 포함하면 충돌이 일어날 수 있기 때문이다. [[https://isocpp.org/wiki/faq/coding-standards#using-namespace-std|출처]]] [*endl 많은 헬로월드 예제 코드들이 '''std::endl'''을 사용하지만, '''std::endl'''은 그저 불필요한 오버헤드가 추가된 개행문자일 뿐이고, 단 한줄 짜리 출력 프로그램의 스트림에 이것이 추가되면 연산량이 늘어나기만 한다. '''std::endl'''을 빼고 만들어보자.[[https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rio-endl|출처]] 다만, 개행문자가 없는 경우 운영체제에 따라서 내용이 실제로 출력되지 않을 수 도 있음에 주의.] {{{#!syntax cpp import ; int main() { std::cout << "Hello, world!"; } }}}[* C++20부터 사용할 수 있는 모듈을 사용한 출력 예시]}}} > '''“A light-weight abstraction programming language”''' > '''“가볍게 추상화한 프로그래밍 언어”''' >---- > - Bjarne Stroustrup [[C(프로그래밍 언어)|C언어]]와 조상을 공유하고 [[객체 지향]] 및 일반화 프로그래밍과 같은 멀티 패러다임을 지원하는 [[프로그래밍 언어]]이다. C언어의 문법과 기능을 대부분 사용할 수 있다.[* 간단히 설명하자면, C언어의 확장판이라고 보면 된다. 만약에 위의 헬로 월드 예제가 귀찮거나 C언어풍으로 쓰고 싶다면 그렇게 해도 좋다.] 1979년에 C언어에서 직접적으로 파생된 C with Classes라는 이름의 언어로 시작되었다가, 1983년에 지금의 이름을 갖게 되었다. 쓸 때는 C++라고 쓰지만 읽을 때는 시 플러스 플러스, 혹은 줄여서 시플플이라고 읽는다. [[한국]]에서는 사실상 속어에 가까운 '''씨쁠쁠'''로 불린다. '씨뿔뿔'로 발음되는 경우도 많다. 나이가 꽤 있는 교수 중에는 간혹 '시 더블 플러스'라고 읽는 경우도 있다. 미국에서는 그냥 씨 플러스 플러스 또는 확장자 명인 CPP(시피피)라고 읽는다. 한국에서 [[컴퓨터공학과]]를 다닐 경우 배울 수 있는 프로그래밍 언어 3개 중에 속한다. 나머지 둘은 [[C(프로그래밍 언어)|C]]와 [[Java]].[* 그러나 실상 C++11 이후의 모던 C++ 기능을 가르쳐주는 곳이 매우 드물어서 C++의 객체 지향만 조금 배울 수 있다. C++의 객체 지향은 다른 언어에 비해서 좀 더 복잡하다. 제대로 다루기 위해서는 별도의 서적을 참고하는 것이 권장되며, 실제 프로젝트의 코드에 대해 탐구하고 경험해보는 것이 좋다. C++를 모르면 2학년부터는 강의를 전혀 이해할 수 없기 때문에 의외로 많은 학생들이 중도포기를 하게 되며 컴공을 나왔는데도 프로그래밍을 전혀 못하는 학생들이 수두룩하게 발생하고 있다.] 학교마다 다르지만 1학년때 아예 다른 언어를 배우고 2학년 1학기때 Java, 2학기때 C++ 과목이 생기는 곳도 있다. 예시로 [[서울대학교]]의 경우에는 1학년 1학기에 [[Python]]으로 코딩을 입문하고, 1학년 2학기에 [[C(프로그래밍 언어)|C언어]]로 어느 정도 큰 프로젝트를 짜 보고, 2학년 1학기에 C++과 [[Java]]를 배우면서 객체지향 프로그래밍을 배우게 된다. 온라인 상으로 코드를 실행시켜 보고 싶다면 [[https://repl.it/languages/cpp|여기]]로. == 상세 == 덴마크의 컴퓨터 과학자 비야네 스트로스트룹(Bjarne Stroustrup)[* 덴마크인이라 이름 역시 [[덴마크어]]이며, 북유럽권 언어가 그렇듯이 영미권에서도 상당히 난해하게 들리는 이름이다. 스트로스트룹이라는 이름은 그냥 알파벳 철자대로 영미식 발음으로 읽은 것이라 원래 발음과는 상당히 다르다. 이름 발음을 물어 보는 경우를 하도 많이 들었는지 아예 본인이 자기 사이트에 wav 파일로 이름 [[http://www.stroustrup.com/bs_faq.html#personal|읽는 법]]을 올려두었다. 한국 웹 역시 그냥 영미권 발음대로 적거나 아예 음역조차 하지 않고 알파벳 그대로 적어두기도 한다.]가 [[C(프로그래밍 언어)|C]]언어를 바탕으로 만들었다. C 언어에서 {{{++}}}라는 것은 원래 값에 1을 더해서 대입하라는 뜻이다.[* 간단하게 'C=C+1' 정도 의미다. [[수학교육과]] 등에서는 '다음수'라는 개념으로 가르친다.] C는 B 언어를 계승한다는 의미에서 C가 되었는데 왜 D가 아니라 C++가 되었냐 하면, C 언어를 거의 그대로 두고 필요한 만큼만 향상시켰기 때문이라고 한다. 비슷한 이름인 [[C\#]]과의 차이도 이러한 점에서 나타난다. 한편 C++ 커뮤니티는 C++가 단순히 [[객체 지향 프로그래밍|객체 지향 프로그래밍 언어]]라고 분류되는 것을 오류라고 여긴다. C++의 제작자인 Bjarne Stroustrup은 '''C++가 단순한 객체 지향 프로그래밍 언어가 아니'''라고 설명했다. [[http://www.stroustrup.com/oopsla.pdf|#]] C++는 프로그래머의 자유도가 높은 언어로써, 객체 지향이나 절차 지향 등의 설계에 제한을 두지 않는다. C++에서는 객체지향 프로그래밍 패러다임과 동등한 강도로 일반화 프로그래밍 패러다임을 강조하고 있다. 스트로우스트루프의 책 The C++ Programming Language 4판을 참고하면 "3부 추상화 메커니즘" 단원의 절반(7개 섹션)은 객체지향, 나머지 절반(7개 섹션)은 일반화 프로그래밍에 할애하고 있다. 그리고 "4부 표준 라이브러리" 단원의 1/3이 [[표준 템플릿 라이브러리|STL]]이고, 2/3는 나머지 표준 라이브러리를 설명하고 있다. 쉽게 말하자면 '''C++는 기존의 C 문법이 대표하는 절차적 프로그래밍, 클래스가 대표하는 객체지향 프로그래밍, 템플릿이 대표하는 일반화 프로그래밍의 세 기둥으로 지지되고 있는 언어'''이다. 유사한 의견으로 Effective C++의 저자 스콧 마이어스는 C++가 4가지 하위언어의 연합체라고 언급한다. C, 객체지향 C++, 템플릿 C++, [[표준 템플릿 라이브러리|STL]].[* [[GCC]]의 libstdc++, [[Clang]]의 libc++로 대표되는 현재의 모던 C++ 표준 라이브러리가 스테파노프(Stephanov)와 리(Lee)가 1990년대에 개발한 Standard Template Library(STL)의 아이디어를 많이 수용한 것은 사실이나, C++의 표준안 그 어디에도 STL이라는 표현은 등장하지 않는다. Effective C++의 저자인 스콧 마이어스(Scott Meyers)와 같이 나이 많은 거장 프로그래머들이 1990년대의 관습 그대로 STL이라는 용어를 자신의 저작물에 지속적으로 사용하는 바람에 STL이라는 용어가 아직도 널리 사용되고 있으나, STL과 C++ 표준 라이브러리의 차이가 뭔지 묻는 수많은 구글 검색 결과가 보여주듯이 초보자들에게 꽤 커다란 혼돈을 주는 요소이다. 스테파노프와 리가 직접 작성한 Hewlet-Packard 버전의 STL이나, 실질적으로 더 널리 쓰이던 Silicon Graphics 버전의 SGI STL은 더 이상 관리가 되지 않고 방치가 된 지 오래이고, SGI STL을 계승하려고 노력하던 STLPort도 개발이 중단되었다. 결국 초창기 STL의 구현체는 모두 사라지고 그 아이디어만이 살아남아서 C++의 표준 라이브러리에 흡수되었으므로, STL을 따로 떼어 지칭하는 것이 이제 의미가 없다. 각각의 '하위 언어' 들의 규칙과 구현이 전부 다르므로 복잡도가 상당히 높으며 'C with classes'라는 초기 명칭과는 한참 차이가 날 정도로 확장되었다.] == 특징 == === C와의 차이점 === 초기 C++가 C언어를 기반으로 시작했기 때문에 지금도 대부분의 C 프로그램은 C++ [[컴파일러]]에서도 문제없이 컴파일된다. 초기 C++ 컴파일러는 일단 C++ 코드를 C로 변환하고 그걸 C로 재컴파일하는 방식을 사용했을 정도. 다만, C가 항상 C++의 부분집합이었던 것은 아니라 지금도 구조체같이 몇몇 부분에서 차이점이 있다. "잘 짜인 C 프로그램은 C++ 컴파일러로 컴파일할 수 있어야 한다"는 말도 1999년에 C99 표준이 나오면서 틀린 말이 되었다. 순수 C 소스 코드를 C++로 컴파일 할 때 문제의 여지가 생길 가능성이 있다. 그러나 C++는 C와 프로그래밍 패러다임이 크게 다르다. C는 태초부터 절차 지향으로 설계된 운영체제를 만들기 위한 언어였으며, 지금도 메모리와 각종 저수준(low level) 프로그래밍을 위한 언어이기 때문이다 [* 다만 C에서 C++의 방법론을 따라가지 못하는 건 아니다. [[GNOME]]의 핵심 라이브러리인 Glib이 대표적으로, C 언어로 객체지향을 짜도록 구성되어 있다. incomplete type declaration 트릭을 이용하여 캡슐화를 흉내내고, 매크로와 컴파일러 확장을 이용해서 type-generic function을 만들어 쓰고, struct hack을 이용하여 vector를 흉내 내는 식이다. 그러나 저런 트릭은 언어 차원에서 정식으로 제공한다기보다 말 그대로 '트릭'에 가까우므로 이상한 조건들이 붙는 경우가 종종 있고, 그것들을 정확히 파악하고 있지 않으면 상당히 찾아내기 어려운 에러를 내는 경우도 많다.]. 반면 C++는 절차 지향, 객체 지향, 일반화 프로그래밍, 함수형 프로그래밍을 모두 지원하는 언어다. 그래서 C로 작성된 프로그램에서 C++ 방식으로 코딩하려면 해당 코드에서 C++에 새로 도입된 것을 추가하는 게 아니라, 설계부터 시작해서 완전히 새로 해야 하는 경우가 많다. 곧 C를 알고 있다고 C++를 쉽게 할 수 있는 것은 아니다. 세간에서 C, C++ 중 어디가 쉽다라던가 하는 건 어디까지나 간단한 프로그램의 예일 뿐이다. 또한 C++의 객체지향이 다른 객체지향 언어에 비해 이해하기가 만만한 개념이 아닌 데다가 C++의 객체지향은 C언어를 기저에 유지하면서 여러 구성 요소를 추가했기 때문에 신경 써야 할 부분이 많다. 그래서 다른 객체지향 언어에서보다 잘 다루는데 더 많은 공부가 필요하다. C++을 잘 활용하려면 [[표준 템플릿 라이브러리]]까지 배운 다음 자료구조 및 알고리즘, 입출력 스트림, 그리고 적어도 다른 언어에서 일반화 프로그래밍이 쓰이는 것처럼 템플릿을 활용할 수는 있어야 한다. 그래서 C언어를 알고 있는 사람이 C++ 초보자용 교재를 1권 끝내고 프로그램을 만들어 보라고 해도, 대부분 C++의 입출력 객체를 이용하는 정도를 넘지 못하고 절차적 프로그래밍을 그대로 따라가는 영락없는 C 방식에서 벗어나지 못한다. C++에서 C 전향도 만만치가 않다. 절차지향 언어의 사고방식이 머릿속에 굳어버려 코드를 전환하는 데 방해가 된다. C++가 C의 모든 기능을 포괄하고 있으므로 C++를 할 줄 알면 C도 할 줄 안다고 생각하기 쉽지만, 사실 C++가 명시적으로 비교적 간단히 사용할 수 있도록 제공하는 기능들을 C에서는 암묵적으로 여러 가지 수많은 '트릭'을 통해서 쥐어짜내듯이 만들어 사용하는 경우가 많다. 지금도 많은 대학에서 컴공 1학년 1학기 때 C를 먼저 가르치고 빠르면 2학기, 늦어도 2학년에 C++를 가르치지만, 반면에 교수가 절차 지향이 머리에 굳어버린다며 C++와 객체를 먼저 가르치고 C는 아예 건드리지도 않고 다른 하드웨어 관련 학과에서만 가르치는 대학들도 많다. 따라서, 초심자 입장에서는 '''C와 C++는 완전히 다른 언어로 파악하고 접근해야 한다.''' ---- {{{#!syntax cpp struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order) int arr[3] = {[1] = 5}; // valid C, invalid C++ (array) struct B b = {.a.x = 0}; // valid C, invalid C++ (nested) struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed) }}}[* C99에서 추가된 지정 초기자(Designated Initializers)] 그외에 {{{_Generic(type)}}}을 사용한 자료형에 대해 안전한 매크로, K&R 스타일의 함수 정의 구문 [* {{{f(int a, int b) { ... } }}} 를 {{{f(a, b) int a; int b; { ... } }}} 로 작성하는 것.]의 지원 등 C에서만 지원되는 문법이 몇 있다. ---- {{{#!folding 클래스 비교 코드 {{{#!syntax cpp // C 스타일 struct GameObject { long id; float x, y, z; }; void InitObject(GameObject * obj, long id) { obj->id = id; obj->x = 0; obj->y = 0; obj->z = 0; } void InitObject(GameObject * obj, long id, float x, float y, float z) { obj->id = id; obj->x = x; obj->y = y; obj->z = z; } void MoveObject(GameObject * obj, float x, float y, float z) { obj->x = x; obj->y = y; obj->z = z; } // C++ 스타일 class GameObject { public: constexpr GameObject(const long& new_id, const float& cx = 0, const float& cy = 0, const float& cz = 0) noexcept : id(new_id), x(cx), y(cy), z(cz) {} constexpr void Move(const float& x= 0, const float& y = 0, const float& z = 0) noexcept { this->x = x; this->y = y; this->z = z; } [[nodiscard]] constexpr std::tuple GetPosition() noexcept { return std::tie(x, y, z); } [[nodiscard]] constexpr std::tuple GetPosition() const noexcept { return std::tie(x, y, z); } long myID = -1; // 기본값 float x, y, z; } }}} }}} C의 함수에서 어떤 고정된 객체를 참조하려면 포인터를 포함해 다중의 인자값 전달이 필수적인데 구조체에 전부 때려박더라도 전역 변수가 아닌 이상 적어도 1개는 전달해야 한다. 반면에 C++ 측은 클래스의 {{{this}}}를 활용하면 인자 전달 없이 클래스에 속한 메서드 호출만 할 수 있다. 그외에 기본 연산자 오버로딩, friend를 통해 외부 클래스 또는 함수와 속성 공유를 할 수 있다. ---- {{{#!syntax cpp std::ios_base::sync_with_stdio(false); }}} {{{iostream}}}이나 {{{fstream}}} 등 입출력 스트림이 C의 입출력 함수보다 상당히 느리다. C++의 입출력 스트림은 예외없는 입출력을 위해 {{{try}}}문은 물론 온갖 내부 비트 플래그가 돌아가고 있기 때문이다. 만약 프로그램내에서 {{{std::cin}}}, {{{std::cout}}}등 C++ 스트림[*iostream] 계열만 사용하고 {{{stdin}}}, {{{stdout}}}등 표준 C 스트림[*stdio.h]을 사용하지 않는다면, 각 입출력 스트림간의 동기화를 정지시켜 iostream에 비약적인 속도 향상을 가져올 수 있다 [* 시간 제한이 빡빡한 PS(문제풀이) 쪽에서는 거의 매번 쓰일 정도인 기능이지만 [[Visual Studio]]에서는 의미 없으니 참고. [[https://github.com/microsoft/STL/issues/3669|#]]]. === 객체 지향 프로그래밍 === C++는 '''[[객체 지향 프로그래밍]]'''을 지원하지만, C++의 객체지향은 다른 객체지향 언어에서와는 성격이 좀 다르다. 대다수의 객체지향 언어에서는 많은 부분을 런타임에 처리하며 메모리를 자동으로 관리해준다. C++은 기반 프레임워크가 존재하지 않아 클래스가 어떤 일급 객체가 아니라, 일반 변수처럼 메모리 덩어리일 뿐이다. 최대한 많은 것을 컴파일 타임에 처리하는 것을 지향하고 동적인 메모리 할당을 지양한다. 그리고 결정적으로 메모리 등을 프로그래머가 직접 관리하게 하기 때문에 클래스를 포함해 전반적인 프로그램 설계 자체가 상당한 차이를 보이게 된다. [[Java]] 등의 다른 객체지향 언어에서와 같은 방식으로 C++ 클래스를 디자인하면 거의 틀림없이 런타임 오류 또는 메모리 문제가 발생한다. 특히 [[가비지 컬렉션|쓰레기 수집]]을 지원하는 Java로 입문하여 C++로 갈아타는 테크를 탄 학생이라면 처음에는 메모리가 줄줄 새는 프로그램을 만들게 될 것이다. 반면 C++ 스타일을 숙지하고 다른 객체지향 언어에서 프로그래밍을 하는 경우 특별히 안 될 것은 없지만, 해당 언어의 원어민 스타일로 작성한 코드에 비해 시간이 많이 걸리고 클래스 및 프로그램 구조가 지나치게 경직되는 경향이 있다. 소멸자가 호출되지 않는 등의 차이점은 있지만 심각한 문제가 되는 경우는 많지 않다. 이러한 차이가 생기는 것은 대부분의 객체지향 언어는 직접적으로 [[Smalltalk]]의 영향을 받은 반면, C++는 Smalltalk보다 먼저 객체지향의 초보적인 개념을 제시한 시뮬레이션 전용 언어인 [[Simula]]에서 직접 영향을 받았기 때문이다. [* Smalltalk에서는 모든 클래스는 최상위 클래스를 상속받으며, 클래스가 가진 모든 [[메타]] 정보를 어디에서나 접근할 수 있다. Smalltalk의 클래스는 편의상 자료형으로 취급되지만 사실 클래스도 어떤 객체로부터 생성되는 인스턴스다. 클래스에 대해 메타 정보를 서술하는 메타클래스가 존재하며, 이를 외부 인터페이스로 내보이는 Reflection을 구현한다. 즉 Smalltalk의 클래스는 네이티브하게 런타임 인스턴스의 속성만 정의해놓은 것이 아니라, 클래스도 {{{new GameObject}}} 마냥 생성되는 인스턴스일 뿐이다.] 이는 C 프로그램과의 호환성을 고려한 결과이기도 하지만, 기본적으로 C++에서 프로그램의 성능을 희생시키지 않기 위해서였다. Smalltalk의 경우 당시의 기술적 한계도 있고 해서 C보다 대체로 수십 배 정도 느렸고, 이는 당시 C++에서 지향하는 결과가 아니었기 때문이다. 이런 차이 때문에 Java, C# 등의 언어를 먼저 접한 프로그래머들은 C++의 객체지향이 짝퉁이라며 싫어하기도 한다. 하지만 순수 객체지향 프로그래밍 언어임을 전면에 내세우는 Java나 C#과 달리, 객체지향 패러다임'''도''' 지원하는 다중 패러다임 언어인 C++ 입장에서는 얼마나 순수하게 객체지향의 이상을 잘 따르는가보다, C++가 제공하는 온갖 패러다임까지 아우르는 내적 일관성이 더 중요하므로 해당 언어들과 C++를 동일선상에 놓고 비교할 수는 없다. 대표적인 예시로 인자로 전달하는 값은 기본적으로 복사되는 것, 함수 내부에서 객체를 동적으로 생성해서 그 객체의 포인터를 반환하는 것이 있다. C++에서는 메모리 문제를 일으키기 쉬운 방식이라서 설계 단계부터 이런 동작을 배제해야할 필요가 있다. 거의 대부분의 프로그램은 실행되고 있는 현재 [[함수|문맥]]에서의 지역 변수를 [[스택(자료구조)|스택 영역]]에 저장하는데, 한 문맥의 실행이 완료되면 그 함수의 스택 영역 변수를 위한 메모리는 모두 삭제된다. 그리고 이 변수는 쓰레기 데이터로 남아있다가 다른 함수를 호출할 때 덮어씌워진다. 공유 자원과 같은 특정 데이터를 유지하려면 힙 영역에 할당해야 한다. 가비지 컬렉션이 자동으로 되는 언어에서는 언어 설계 차원에서 메모리에 대한 걱정을 덜기 위해 사용되지 않는 메모리를 자동으로 해제 해주기 때문에 매우 흔히 사용되는 방법이다. [* 다만 해당 언어들조차 파일 입출력에서는 별도의 소멸자, 파괴 메서드, 예외 처리의 도움이 필요하다.] 그러나 문제가 생기는 건 오래된 스타일의 C++ 코드에서나 그런 것이고, Modern C++에서는 스마트 포인터를 사용하거나 이를 이동 연산[* 원본 객체가 필요없는 경우, 새로 객체를 생성할 때 깊은 복사 없이 기존 객체의 메모리를 재활용할 수 있도록 하는 기능이다.]을 통해 그들과 완전히 같은 구조를 구현 가능하기 때문에 더 이상 해당이 없는 사항이다. 예를 들어 한 동적 객체를 여러 소유권자가 공유하는 경우 (참조 대상에 대한 횟수를 기록하는) 레퍼런스 카운팅을 지원하는 {{{std::shared_ptr}}}를, 단일 소유자만 존재하는 경우 {{{std::unique_ptr}}}를 사용한다 [* 또한 레퍼런스 카운팅으로 인해 발생하는 {{{std::shared_ptr}}}의 순환 참조 문제를 피하기 위해서 참조만 할 뿐 소유권은 지니지 않는 {{{std::weak_ptr}}}도 지원한다. {{{std::weak_ptr}}}은 객체 대신 {{{std::shared_ptr}}}을 참조한다고 보면 된다. 하나의 {{{std::shared_ptr}}}과 다수의 {{{std::weak_ptr}}}을 사용하면 여러 소유권자가 하나의 객체를 공유하면서도 순환 참조 문제를 해결할 수 있다.]. === 일반화 프로그래밍 === C++에서는 [[템플릿]]을 이용한 [[https://en.wikipedia.org/wiki/Generic_programming|일반화 프로그래밍]](Generic Programming)이 매우 폭넓게 사용된다. 특히 C++11을 시작으로 하는 모던 C++는 일반화 프로그래밍을 빼고 이야기하는 것이 불가능하다. 당장 매우 널리 사용되는 문자열 클래스 {{{std::string}}}만해도 실제로 들여다보면 {{{std::basic_string>}}}와 같은 형태의 클래스 템플릿의 특수화에 지나지 않는다. 일반화 프로그래밍의 결과물으로는 C++ 표준 라이브러리의 일부분으로 포함된 컨테이너, [[표준 템플릿 라이브러리|STL]] 같은 것들이 있으므로, C++ 표준 라이브러리를 사용하는 순간 일반화 프로그래밍의 도움을 받는 것이다. 따라서 '나는 C++를 사용하지만 템플릿을 이용한 일반화 프로그래밍은 어려우니까 패스하겠다'는 말은 애초에 성립하지 않는다. 또 다른 예시로, C와는 달리 배열 생성에 포인터 혹은 []을 사용하는 배열 대신에 {{{std::vector}}}를 기본으로 사용하라고 가르치는데, 이 {{{std::vector}}} 또한 저장될 원소의 자료형을 {{{std::vector}}}와 같이 템플릿 매개변수로 받아들이는 템플릿 클래스다. 중급 이하 개발자는 라이브러리를 그냥 가져다 쓰면 되고, 직접 만들어 쓸 필요는 없다. 이는 중급 개발자 정도로는 템플릿을 사용한 일반화 프로그래밍 기법을 정확하게 적용하는 것이 무척 까다롭고, 디버깅할 때 이해 불가능한 컴파일러 메세지를 받게 되는 경우가 많아 오류를 수정하기도 어렵기 때문이다. 현재는 C++20에서 {{{concept}}}의 등장으로 이전보다 상대적으로 알아보기 쉬운 템플릿 작성이 가능해졌고, 컴파일 오류 메세지또한 좀 더 이해하기 쉽고 간결하게 표시되게 되었다. 이런 이유로 C++ 학습 초반에 템플릿 프로그래밍을 직접 하는 것을 피하고 기껏해야 컨테이너 클래스만 사용하는 습관이 들다 보니, C++는 단순히 객체 지향 언어이고 [[표준 템플릿 라이브러리|STL]]이라는 템플릿 라이브러리를 덤으로 쓸 수 있는 정도라는 오해가 널리 퍼진 것이라 생각된다. 하지만, 특수 목적의 컨테이너를 설계하거나 범용 라이브러리를 설계하는 수준의 고급 개발자가 되려면 템플릿을 사용한 일반화 프로그래밍을 해야 한다. [[Java]]나 [[C\#]]에서 찾아볼 수 있는 Generics가 지금 설명한 C++ 일반화 프로그래밍의 아주 제한된 형태의 적용례에 해당한다. ==== 메타 프로그래밍 ==== >TMP ('''T'''emplate '''M'''eta '''P'''rogramming). 템플릿을 사용한 메타 프로그래밍 [[https://en.wikipedia.org/wiki/Template_metaprogramming|Wikipedia]] C++에서는 템플릿을 사용해 메타 프로그래밍을 할 수 있다. 여기서 [[메타]]란 [[메타데이터|Meta Data (서술 자료)]]를 의미한다. 값 뿐만 아니라 프로그램, 바이너리, 속성 그 자체도 데이터로 취급한다. 가령 [[Python]]의 메타 클래스는 '클래스'를 생성하며 [* '클래스의 인스턴스'가 아니다.] 클래스의 바이트 크기, 필드, 메서드 등을 정의한다. 또한 [[Python]], [[C\#]], [[Swift]] 등 많은 언어가 {{{int}}}, {{{double}}} 같은 원시 자료형도 클래스로 보여준다. 하지만 C++은 그럴 수가 없다. C++의 클래스는 메모리 덩어리고, {{{int}}}라는 단어 자체에는 그저 컴파일러가 {{{int}}}를 메타 정보로 가진 객체는 32비트 크기로 읽겠다, 말고는 아무 의미도 없다. 다른 언어는 '정보[* 변수]의 메타 정보[* 자료형]의 메타 정보[* 메모리 정렬 방식, 사용자가 접근할 수 있는 메모리 영역, 바이트 크기, '''메서드의 이름과 코드''', '''필드의 이름과 정보''']를 언어 자체적으로 가져오고, 심지어 정의할 수도 있다. [[C\#]], [[Javascript]], [[Python]]이 대표적인 예시다. 반면 C++에선 클래스가 그냥 메모리 덩어리고 메서드도 그냥 쓰기 좋게 만든 함수 포인터다. 전처리기, 컴파일러 확장, 미리 쓰여진 코드에 의존할 뿐 자료형 자체에 대해서는 아무것도 알 수 없다. {{{sizeof}}}, {{{alignof}}}, {{{alignas}}} 키워드가 있지만 부족하다. 그리고 직접 클래스 자체의 속성을 정의하려면 {{{using}}}, {{{static}}} 변수를 사용해 일일히 모든 클래스에 메타 정보를 기입해야 한다. 이 방법은 임의의 클래스, 그리고 원시 자료형에는 적용할 수 없는 문제가 있다. 이 간극을 메우기 위해 대신 템플릿을 사용해 메타 프로그래밍을 시도하는 것이다. 같은 템플릿 문법을 사용하지만 앞 문단의 일반화 프로그래밍과는 엄연히 목표가 다르다. 일반화 프로그래밍이 자료형을 드러내어 컴파일 시기에든 실행 시기에든 코드 확장 및 범용성 증대를 목표로 한다면, 메타 프로그래밍은 명시해야할 자료형을 숨기고, 어떤 자료형에 대해 그 자료형에 대한 정보를 기술하는 것을 목표로 한다. 이는 일반화 프로그래밍과는 다르게 확장이 아니고 특정 자료형에만 코드를 생성하도록 만든다. * SFINAE('''S'''ubstitution '''F'''ailure '''I'''s '''N'''ot '''A'''n '''E'''rror): 함수를 오버로딩 하는데 있어 조건에 따라 일부러 오류가 발생하는 템플릿의 구현 코드를 발생시켜 틀리지 않은 특정 구현만 선택되게 만드는 테크닉이다. C++의 템플릿은 자료형의 상태를 서술하기 위해 분명히 존재하지만, 실제로 인스턴스화되는 시점은 런타임 직전 사용자의 코드에서 실제 자료형을 가져와야 한다. C++ 문법의 틈새에 존재하는 문법 오류이지만 런타임 오류가 아닌 상황을 적극 활용하는 것이다. 직역하면 '대입 실패는 오류가 아니다'라는 뜻이다. ---- 그렇다면 C++에서 그냥 메타 프로그래밍이라고 안하고 굳이 앞에 템플릿이라고 붙인 이유가 있을까? C++의 템플릿은 독특한 특성을 많이 가지고 있다. 템플릿 매개변수는 반드시 컴파일 시점에 결정된다. 템플릿 매개변수는 자료형 뿐만 아니라 값도 전달할 수 있다. 템플릿의 실제 코드는 늦게 평가된다. 템플릿은 분명 존재하는 C++ 코드이지만, 실질적으로 전처리기 구문과 다름이 없다. 실행 시점에는 이미 바이너리로 모든 경우에 대해 완성된 상태가 되므로 컴파일 이후에 실행 시간에는 아무 영향이 없다 [* 이를 '결정적'이라고 한다.]. 컴파일러가 예측할 수 없는[* 이를 '비결정적'이라고 한다.] 동적 메모리 할당, 메모리 해제, 네트워크 단의 작업, 그리고 운영제체 호출을 제외하면 모두 상수 시간에 결정할 수 있다. 곧 C++의 메타 프로그래밍은 자료형의 상태를 기술하는 것에 더해서, 할 수 있는 작업은 컴파일러를 고문해 모조리 미리 처리하는 것이 지상과제가 된다. 이는 C++의 템플릿 문법이 컴파일 시간에 [[튜링완전|Turing complete]]하기 때문에 이런 일이 가능한 것이다. C++ 안에 컴파일러 전용의 또 다른 언어가 숨어있는 것과 같은 상황이다. 다른 언어에서는 비슷한 것도 찾기 힘들다. * Expression Templates: 디자인 패턴의 일종인 Proxy pattern 기반의 Lazy evaluation이 적용되는 효율적인 계산 코드를 컴파일 시점에 생성하는 기법이다. 일반적으로 연산 도중의 임시 객체 생성 문제를 이 기법을 통해 해결하는 경우가 있다. RVO(Return Value Optimization)를 감안하더라도 C++ 특성상 연산자를 활용하는 과정에서, 직접적인 연산을 시도하면 임시 객체의 생성을 완전히 막을 수는 없기 때문이다. 배우기 어렵고 알아보기도 힘들고, 실행 성능을 포기하지 않는 대신 컴파일 시간을 '''심각하게''' 포기했다. 심지어 디버깅도 힘들다. 다만 디버그 문제는 자기가 원하는 템플릿에 맞춰 중단점을 거는 게 가능해지는 등 많이 개선된 편이다. 당연히 [[템플릿]]에 대하여 깊은 이해가 없다면 아예 이해할 수가 없는 개념이기도 하다. 그래도 알아두면 은근 써먹을 데가 많다. C++에서는 단순히 특정 자료형의 속성 선언부터 함자 클래스[* '''Functor, Niebloid, Function Object'''. () 연산자 오버로딩을 통해 함수 인터페이스를 구현한다]를 이용한 방문자 패턴, 트레잇, 타입 리스트, CRTP, mixin 등 수많은 고성능 디자인 패턴은 C++에서는 TMP의 도움 없이는 시도조차 할 수 없다. C++17 기준으로 쓰여진 [[https://www.amazon.com/C-Templates-Complete-Guide-2nd/dp/0321714121|C++ 템플릿 기본서]] 는 그 쪽수가 800쪽을 넘는다. 예를 들어, [[피보나치 수열]]을 계산하는 코드는 다음과 같이 쓸 수 있다. {{{#!syntax cpp constexpr size_t fibonacci(size_t n) noexcept { return (n < 2) ? n : fibonacci(n - 1) + fibonacci(n - 2); } constexpr size_t result = fibonacci(7); // 13 }}} 아래는 템플릿을 활용해 컴파일 시점에 계산하는 코드이다. [* GCC와 같은 컴파일러는 최적화 옵션을 넣지 않을 경우 {{{constexpr}}} 함수를 컴파일 시점에 계산하지 않는데, 아래와 같이 템플릿을 섞어서 사용하면 확실히 컴파일 시점에 계산된다.] {{{#!folding 템플릿 코드 {{{#!syntax cpp template struct Fibonacci { static constexpr size_t Value = Fibonacci::Value + Fibonacci::Value; }; template<> struct Fibonacci<0> { static constexpr size_t Value = 0; }; template<> struct Fibonacci<1> { static constexpr size_t Value = 1; }; int arr[Fibonacci<7>::Value]; }}} C++17의 {{{if constepxr}}} 또는 C++20의 {{{consteval}}}을 활용하면 더 간결하게 쓸 수 있다. {{{#!syntax cpp template constexpr size_t Fibonacci() noexcept // C++20 이후에는 consteval로 지정해도 문제없다. { if constexpr (N < 2) return N; else return Fibonacci() + Fibonacci(); } int arr[Fibonacci<7>()]; }}} }}} 실무에서 가장 많이 쓰이는 테크닉 중 하나로 CRTP (Curiously Recursive Template Pattern) 패턴이 있다. 추상 클래스를 구현할 시 가상 함수 호출에 따른 오버헤드를 막고자 고안되었다. 이는 다음과 같이 쓴다. {{{#!folding CRTP 예제 코드 {{{#!syntax cpp template class Base { constexpr void Function() noexcept(noexcept(Cast()->Function())) { Cast()->Function(); } protected: constexpr Derived* Cast() noexcept { return static_cast(this); } constexpr const Derived* Cast() const noexcept { return static_cast(this); } }; class CRTPDerived1 : public Base { public: void Function() noexcept // Base::Function은 상수식이 아니고 noexcept(true) { std::print("CRTPDerived1"); } }; template class CRTPDerived2 : public Base> { public: constexpr void Function() // Base::Function은 상수식이지만 noexcept(false) { throw "CRTPDerived2"; } }; }}} }}} 파생 클래스를 템플릿 인자로 기반 클래스에 넘겨줌으로써, 기반 클래스는 멤버 함수의 동작을 각 파생 클래스에 대해 특수화할 수 있다. '''템플릿 인자는 컴파일 타임에 알 수 있는 내용이므로, 프로그램 내에서 기반 클래스가 어떤 파생 클래스로 인스턴스화되는지 컴파일 타임에 알 수 있다.''' 따라서 런타임 가상 함수 디스패치 및 그에 따른 오버헤드가 필요없다! 이는 멤버 함수 호출이 잦은데 상속은 구현하고 싶은 경우에 성능상으로 엄청난 차이를 낸다. === 용도 === 본래 C는 [[유닉스]]라는 OS를 만들기 위해 어셈블리 대체용으로 만들어진 언어라, 머신 컨트롤의 저수준 작업이 주된 임무 중 하나였다. C++은 그 이름에서 보이듯이 사실상 C를 대체하기 위한 언어였기 때문에 여러 가지 고수준의 추상 기능들을 집어넣어 추상적인 시스템 위에서만 노는 게 아니라, 저수준의 기계 제어까지 가능한 추상화라는 요상한 철학을 지지한다. 뭔가 딜레마 같지만 C++과 비슷한 정도의 고수준 기능을 제공하는 언어는 절대로 C++만큼 복잡하지 않다. 여타 언어들이 객체 지향 프로그래밍을 구현하면서 동적 바인딩(C++의 가상 함수)을 디폴트로 쓰고 [[쓰레기 수집]]을 지원할 때 C++은 정적 바인딩을 디폴트로 하고 수동 메모리 관리를 유지한 것이다. 일반적으로 추상화 수준이 높은 언어일수록 프로그래머 머리로 해야할 구체적인 것들을 컴퓨터가 대신 해주고, 이것이 항상 최적화된 방식은 아니기 때문에 프로그램의 실행 속도가 상대적으로 느리게 마련이지만, C++ 프로그램은 위와 같은 이유로 성능 하락이 거의 없다. C++과 비슷한 정도의 기능을 가진 언어 중에서 C++만큼 빠른 실행 성능을 내는 언어 구현은 흔치 않다. 대신 프로그래머가 언어의 이상한 부분까지 신경쓰지 않으면 안 되는 디자인이 되어 제대로 쓰는 게 굉장히 어려운 언어가 되고 말았다. 추상화의 가장 큰 이유와 장점은 그것을 추상화시킴으로서 그 아래 감춰진 디테일한 부분을 신경쓸 필요가 없게 만드는 것이다. 따라서 저수준 제어와 고수준 추상화의 두 가지 개념은 서로 완전히 충돌하는 부분이다. C++는 저수준 제어를 포기하지 않았기 때문에 프로그래머는 C++로 저수준 작업을 할 때는 디테일한 부분까지 신경써야만 한다. 반대로 C++에서 고수준 기능을 사용하려면 프로그래머가 저런 부분을 감안하여 C++ 방식으로 변형되어 적용된 코딩 문화를 알고 있어야 한다. 애초에 C 언어의 기능을 모두 포함했던 이유는 생산성과 함께 C를 대체하겠다는 두마리의 토끼가 목적이었기 때문이다. 그러나, OS를 만드는 [[유닉스]] 커뮤니티들이 C++로의 전환을 거부해서 한마리 토끼는 놓쳤지만 그럼에도 [[Java]]라는 강력한 언어가 등장하기 전까지는 C에 비해 압도적인 생산성으로 어플리케이션 소프트웨어 쪽에서 순식간에 대세언어가 되었으나 Java의 등장과 각종 스크립트 언어들의 부상, 그리고 [[비주얼 베이직]]에서 [[Python]]으로 이어지는 하이퍼 고 생산성 언어들의 등쌀에 현재는 포지션이 좀 애매해진 경향이 있다. 애초에 C의 기능을 전부 포함했던게 공짜로 된 것이 아니라 그만큼 복잡해지고, 컴파일 속도까지 희생하는 역효과도 포함하고 있었고, 덕분에 Java나 C# 등의 기타 고생산성 언어들에 비해 생산성은 떨어지면서 정작 노리고 있던 C의 대체도 날아간 상황. 현재는 클라이언트 쪽에서는 성능이 엄청나게 중요하면서 동시에 개발속도도 크리티컬한 게임[* C#을 스트립트 언어로 쓰는 유니티 엔진도 내부 소스는 C++로 이루어져 있다. 즉, 스크립팅용으로만 [[C\#]]을 쓰는 셈이다. 심지어 C#으로 짜놓은 코드를 C++로 변환하는 [[https://docs.unity3d.com/kr/2018.1/Manual/IL2CPP.html|IL2CPP]]라는 기능도 제공한다. ], 포토샵, 웹 브라우저 등의 데스크탑 어플리케이션, 업무 연속성이 중요한 [[금융]]권 IT망 및 [[금융공학]]에서 주로 사용된다. 사실 그 외에도 퍼포먼스를 중시하는 경우 여전히 많이 사용된다. [[구글]]에서도 상당히 많이 사용하는 언어이다.[[https://web.archive.org/web/20111228034932/http://panela.blog-city.com/python_at_google_greg_stein__sdforum.htm|#]] 이미 구글에서 2009년에 개발한 컴파일 기반 언어인 [[Go(프로그래밍 언어)|Go]]가 나와 있었는데도 [[텐서플로]]가 C++로 개발되었다는 점이 하나의 예시이다. == [[/문법|문법]] == [include(틀:상세 내용, 문서명=C++/문법)] == 역사 == === 모던 C++ === 각종 주요 컴파일러들과 모던 C++의 호환성은 [[http://en.cppreference.com/w/cpp/compiler_support|여기서]] 볼 수 있다. ==== C++26 ==== ||<:>C++26 모듈 목록|| ||<:> {{{#!wiki style="margin: 0 -10px" {{{#!folding [ 펼치기 · 접기 ] ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}}}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>~~{{{}}}~~||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}}}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}}}}}||<:>{{{-1 {{{}}}}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}} }}}||<:>{{{-1 {{{}}} }}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| }}} }}} || ==== C++23 ==== ||<:>C++23 모듈 목록|| ||<:> {{{#!wiki style="margin: 0 -10px" {{{#!folding [ 펼치기 · 접기 ] ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}}}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>~~{{{}}}~~||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}}}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}}}}}||<:>{{{-1 {{{}}}}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{-1 {{{}}} }}}||<:>{{{-1 {{{}}} }}}||<:>{{{}}}|| ||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| ||<:>{{{-1 {{{}}}}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}||<:>{{{}}}|| }}} }}} || 호출 스택 추적(리플렉션), executor 등이 추가될 예정이었으나, 전세계적인 [[COVID-19|코로나 바이러스]] 판데믹으로 인해 표준 위원회의 작업이 더뎌지고 있다. 주로 다른 언어의 장점을 취합하여 C++20까지의 결점이나 부족했던 기능들 등을 보완하는 패치이다. 1. 개별 헤더가 아닌 표준 라이브러리 전체에 대해 모듈 지원. {{{import std;}}} 또는 호환성을 위해 {{{import std.compat;}}}으로 사용 가능함 1. {{{}}} 1. {{{}}} 너무 낡고 저성능인 {{{iostream}}}을 대체하는 출력 라이브러리. 콘솔 뿐만 아니라 FILE 포인터로 출력이 가능하다. 1. {{{}}} 불편한 다중 배열을 대체하는 클래스. {{{std::span}}}과는 다르게 실제 데이터를 소유할 수 있다. 1. {{{}}} 참조로 오류 코드를 전달하는 방식 및 예외 방식을 대체하는 클래스. 모나드 연산을 지원한다. 1. {{{}}} [[코루틴]]을 통해 비동기 범위 연산을 지원하는 클래스. 범위 기반 for문을 비동기적으로 실행할 수 있다. 1. 정적 [] 연산자 1. 정적 () 연산자 1. 인덱스 참조 연산자 [] 에 여러 개의 인자를 전달할 수 있음 1. 람다 표현식에 특성을 사용할 수 있음 1. {{{constexpr}}} 함수 안에서 {{{goto}}}문을 포함하여 상수 평가식이 아닌 구문을 사용할 수 있음. 왜냐하면 C++23에 와서는 {{{constexpr}}}는 반드시 상수 평가식이 아니라, 컴파일 시간에 실행될 수'''도''' 있는 표현식이기 때문임. 대신 문맥 구분을 위해 반드시 {{{std::is_constant_evaluated()}}}를 사용해야함 1. 완벽한 인자 전달 (Perfect Forwarding)을 위해 {{{std::forward}}} 대신에 {{{auto(glvalue)}}}, {{{auto{glvalue}}}}를 사용할 수 있음 1. 새로운 특성 {{{[[assume(expression)]]}}} ==== C++20 ==== ||<:>