최근 수정 시각 : 2022-08-07 06:55:43

C++

파일:하위 문서 아이콘.svg   하위 문서: C++/문법
,
,
,
,
,
#!wiki style="display: inline; display: none;"
, }}}
프로그래밍 사이트 선정 상위 점유율 프로그래밍 언어 목록
{{{#!wiki style="margin: 0 -10px -5px"
{{{#!wiki style="display:inline-block; margin:0 0 -5px; min-width:25%"
{{{#!folding ⠀[ IEEE Spectrum 2021 ]⠀
{{{#!wiki style="margin:-6px -1px -10px"
IEEE Spectrum에서 집계한 2021년 기준, 웹 분야 상위 10개 프로그래밍 언어
1 Python 2 Java 3 Javascript 4 C# 5 Go
6 HTML 7 PHP 8 Dart 9 Ruby 10 Rust
IEEE Spectrum에서 집계한 2021년 기준, 모바일 분야 상위 10개 프로그래밍 언어
1 Java 2 C 3 C++ 4 C# 5 Swift
6 Dart 7 Kotlin 8 Scala 9 Objective-C 10 Delphi
}}}}}}}}}
⠀[ Stack Overflow 2021 ]⠀
||<table width=100%><width=2000><-10><bgcolor=#FFA500><table bgcolor=#fff,#222> Stack Overflow에서 조사한 2021년 기준 사용률 상위 25개 프로그래밍 언어
1 JavaScript 2 HTML, CSS 3 Python 4 SQL 5 Java
6 Node.js 7 TypeScript 8 C# 9 Bash 10 C++
11 PHP 12 C 13 PowerShell 14 Go 15 Kotlin
16 Rust 17 Ruby 18 Dart 19 어셈블리어 20 Swift
21 R 22 VBA 23 MATLAB 24 Groovy 25 Objective-C
⠀[ TIOBE 2022 ]⠀
||<table width=100%><width=2000><-10><bgcolor=deepskyblue><table bgcolor=#fff,#222> TIOBE에서 선정한 2022년 2월 기준 검색어 점유율 상위 20개 프로그래밍 언어
1 Python 2 C 3 Java 4 C++ 5 C#
6 Visual Basic .NET 7 JavaScript 8 PHP 9 어셈블리어 10 SQL
11 Classic
Visual Basic
12 R 13 Go 14 Fortran 15 Groovy
16 Swift 17 Ruby 18 Perl 19 MATLAB 20 Delphi /
Object Pascal
{{{#!wiki style="margin:0 -10px -5px"
{{{#!folding [ 21위 ~ 50위 펼치기 · 접기 ]
{{{#!wiki style="margin:-6px -1px -10px"
21 Objective-C 22 Prolog 23 Scratch 24 SAS 25 LISP
26 COBOL 27 Rust 28 Ada 29 Dart 30 Transact-SQL
31 PL/SQL 32 ABAP 33 VBScript 34 LabVIEW 35 Julia
36 Scala 37 TypeScript 38 Kotlin 39 Haskell 40 Lua
41 Apex 42 Visual FoxPro 43 Bash 44 Scheme 45 D
46 PL/I 47 Elixir 48 Logo 49 PostScript 50 Ladder Logic }}}}}}}}}
⠀[ PYPL 2022 ]⠀
||<table width=100%><width=2000><-10><bgcolor=green><table bgcolor=#fff,#222> PYPL에서 선정한 2022년 3월 기준 검색어 점유율 상위 20개 프로그래밍 언어
1 Python 2 Java 3 JavaScript 4 C# 5 C/ C++
6 PHP 7 R 8 Objective-C 9 TypeScript 10 Swift
11 MATLAB 12 Kotlin 13 Go 14 Rust 15 Ruby
16 VBA 17 Ada 18 Abap 19 Dart 20 Visual Basic
}}}
프로그래밍 언어 목록 · 분류 · 문법

TIOBE 선정 올해의 프로그래밍 언어 / C++
{{{#!folding [펼치기 / 접기] 없음 2003년
C++
2004년
PHP
}}} ||


파일:C++ 로고.svg
1. 개요2. 특징
2.1. C와의 차이점2.2. C++에서의 객체지향 프로그래밍2.3. C++에서의 일반화 프로그래밍
2.3.1. 템플릿 메타 프로그래밍
2.4. 성능2.5. 장점
2.5.1. 많은 시스템에서 지원하는 언어2.5.2. 성능2.5.3. 수많은 기능
2.6. 단점
2.6.1. 난이도
2.6.1.1. 메모리 관리2.6.1.2. 갈수록 늘어나는 기능
2.6.2. 애매한 위치
2.6.2.1. C언어와의 호환성2.6.2.2. 템플릿의 난해성
2.6.3. 성능 저하 요소
2.6.3.1. RTTI2.6.3.2. 예외
3. 역사
3.1. 클래식 C++
3.1.1. C with Classes (1979년) 3.1.2. C++ (1983년)3.1.3. C++ 2.0 (1989년)3.1.4. C++98 (1998년)3.1.5. C++03 (2003년)
3.2. 모던 C++
3.2.1. C++TR1 (2007년)3.2.2. C++11 (2011년)3.2.3. C++14 (2014년)3.2.4. C++17 (2017년)3.2.5. C++20 (2020년)3.2.6. C++23 (2023년)
4. 방언 및 확장
4.1. GPGPU를 위한 확장4.2. 마이크로소프트의 방언
5. 문법6. 도서7. 관련 문서8. 외부 링크

1. 개요


#!syntax cpp
#include <iostream>

int main () {
    std::cout << "Hello, world!";
}
[1] [2]
#!syntax cpp
import std.core;

using std::cout;

int main() {
    cout << "Hello, world!";
}
[3]

“A light-weight abstraction programming language”
"가볍게 추상화한 프로그래밍 언어"
- Bjarne Stroustrup

C언어와 조상을 공유하고 객체 지향 및 일반화 프로그래밍과 같은 멀티 패러다임을 지원하는 프로그래밍 언어이다. C언어의 문법과 기능을 대부분 사용할 수 있다.[4] 1979년에 C언어에서 직접적으로 파생된 C with Classes라는 이름의 언어로 시작되었다가, 1983년에 지금의 이름을 갖게 되었다.

온라인 상으로 코드를 실행시켜 보고 싶다면 여기로.

2. 특징

덴마크의 컴퓨터 과학자 비야네 스트로스트룹(Bjarne Stroustrup)[5] C언어를 바탕으로 만들었다. C 계열 언어에서 "++"라는 것은 1을 더해서 원래 변수에 대입하라는 뜻이다.[6] C는 B 언어를 계승한다는 의미에서 C가 되었는데 왜 C++가 D가 아니라 C++가 되었냐 하면 C 언어를 거의 그대로 두고 필요한 만큼만 향상시켰기 때문이다. 그래서 C에서 되는 게 C++에서도 된다. 비슷한 이름인 C#과의 차이도 이러한 점에서 나타난다.

C++ 커뮤니티는 C++가 단순히 객체지향 프로그래밍 언어라고 분류되는 것을 오류라고 여긴다. C++의 제작자인 Bjarne Stroustrup은 C++가 단순한 객체 지향 프로그래밍 언어가 아니다 라고 설명했다. # 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.[7]

쓸 때는 C++라고 쓰지만 읽을 때는 시 플러스 플러스, 혹은 줄여서 시플플이라고 읽는다. 한국에서는 사실상 속어에 가까운 씨쁠쁠로 불린다. '씨뿔뿔'로 발음되는 경우도 많다. 연세가 꽤 있으신 교수님 중에는 간혹 '시 더블 플러스'라고 읽는 경우도 있다. 미국에서는 그냥 씨 플러스 플러스 또는 CPP(시피피)라고 읽는다. cpp는 확장자 명이기도 하다.

한국에서 컴퓨터공학과를 다닐 경우 배울 수 있는 프로그래밍 언어 3개 중에 속한다. 나머지 둘은 C Java.[8]

2.1. C와의 차이점

C++의 개발이 C언어를 기반으로 이루어졌기 때문에 대부분의 C 프로그램은 C++ 컴파일러에서도 문제없이 컴파일된다. 초기 C++ 컴파일러는 일단 C++ 코드를 C로 변환하고 그걸 C로 컴파일하는 방식을 사용했을 정도. 다만, C가 항상 C++의 부분집합이었던 것은 아니라서 공통적인 부분에서 몇몇 차이점이 있으며[9], 순수 C 소스 코드를 C++로 컴파일 할 때 문제의 여지가 생길 가능성이 있다.

C++는 C와 프로그래밍 패러다임이 크게 다르다. 그래서 C로 작성된 프로그램에서 C++ 방식으로 코딩하려면 해당 코드에서 C++에 새로 도입된 것을 추가하는 게 아니라, 설계부터 시작해서 완전히 새로 해야 하는 경우가 많다. C++의 객체지향이 다른 객체지향 언어에 비해 이해하기가 만만한 개념이 아닌 데다가 C++의 객체지향은 C언어를 하위 요소로서(항상 그래온 것은 아니지만) 유지하면서 여러 문법 요소를 추가했기 때문에 신경 써야 할 부분이 많다. 다른 객체지향 언어에서보다 잘 다루는데 더 많은 공부가 필요하다. 따라서, 초심자 입장에서는 C와 C++는 완전히 다른 언어로 파악하고 접근하는 것이 좋다.[10]

C++ → C 테크도 만만치가 않다. C → C++는 절차지향 언어의 사고방식이, C++ → C는 객체지향 언어의 사고방식이 머릿속에 굳어버려 코드를 전환하는 데 방해가 된다. C++가 C의 모든 기능을 포괄하고 있으므로 C++를 할 줄 알면 C도 할 줄 안다고 생각하기 쉽지만, 사실 C++가 명시적으로 비교적 간단히 사용할 수 있도록 제공하는 기능들을 C에서는 암묵적으로 여러 가지 수많은 '트릭'을 통해서 쥐어짜내듯이 만들어 사용하는 경우가 많다. GNOME의 핵심 라이브러리인 Glib이 대표적으로 C 언어로 객체지향을 짜도록 구성되어 있다. incomplete type declaration 트릭을 이용하여 캡슐화를 흉내내고, 매크로와 컴파일러 확장을 이용해서 type-generic function을 만들어 쓰고, struct hack을 이용하여 vector를 흉내 내는 식이다. 게다가, 저런 트릭은 언어 차원에서 정식으로 제공한다기보다 말 그대로 '트릭'에 가까우므로 이상한 조건들이 붙는 경우가 종종 있고, 그것들을 정확히 파악하고 있지 않으면 상당히 찾아내기 어려운 에러를 내는 경우도 많다.

그래서 많은 대학에서 컴공 1학년 1학기 때 C를 먼저 가르치고 빠르면 2학기, 늦어도 2학년에 C++를 가르치지만, 교수가 절차지향이 머리에 굳어버린다며 컴공에서 C 대신 C++와 객체를 먼저 가르치고 C는 아예 건드리지도 않고 다른 하드웨어 관련 학과에서만 가르치는 대학들도 많다. 객체지향만 배운다면 Java부터 시작하는 게 낫지만, 포인터 등을 통해 메모리와 각종 저수준(low level) 프로그래밍과 객체지향을 함께 배울 수 있기 때문이다. 컴공의 경우에는 C++를 모르면 2학년부터는 강의를 전혀 이해할 수 없기 때문에 의외로 많은 학생들이 중도포기를 하게 되며 컴공을 나왔는데도 프로그래밍을 전혀 못하는 학생들이 수두룩하게 발생하고 있다. 학교마다 다르지만 1학년때 아예 C를 배우고 2학년 1학기때 Java, 2학기때 C++ 과목이 생기는 곳도 있다. 서울대학교의 경우에는 1학년 1학기에 Python으로 코딩을 입문하고, 1학년 2학기에 C언어로 어느 정도 큰 프로젝트를 짜 보고, 2학년 1학기에 C++과 Java를 배우면서 객체지향 프로그래밍을 배우게 된다. C++ 수업에 잘 적응하려면, C → Java → C++ 순으로 듣는 것을 권장한다.

C를 알고 있다고 C++를 쉽게 할 수 있는 것은 아니다. 두 언어는 추구하는 설계 구조가 상당히 다르기 때문이다. C언어는 절차 지향이지만 C++는 절차 지향, 객체 지향, 일반화 프로그래밍, 함수형 프로그래밍을 모두 지원하는 언어다. 그래서 C언어를 알고 있는 사람이 C++ 초보자용 교재를 1권 끝내고 프로그램을 만들어 보라고 해도 대부분 C 방식에서 벗어나지 못한다. C++ 방식을 잘 활용하려면 STL까지 배운 다음 자료구조와 스트림, 템플릿 정도는 쓸 수 있어야 한다. C++이 C언어의 변형버전은 어느정도는 맞는 이야기긴 하다.

한편, C, C++ 중 어디가 쉽다라던가 하는 건 어디까지나 간단한 프로그램의 예일 뿐이다. 둘의 설계 구조가 다르고 성능상 미묘한 차이밖에 안 난다. C의 함수구조에서는 다중의 인자값 전달이 필수적인데 구조체에 전부 때려박더라도 전역변수가 아닌 이상 적어도 1개는 전달해야 한다. 반면에 C++ 측은 클래스의 this를 활용하면 인자 전달 없이 클래스에 속한 멤버함수 호출만 할 수 있다. 헤더참조 구조가 중복정의로 복잡해지는 건 덤. Node.js 모듈구조를 참고 해 보면 module.exports로만 다른 모듈과 상호작용 할 수 있는데 C 계열은 헤더를 넣으면 그 안에 있는 모든 건 상호작용되고 원치 않으면 소스파일과 분리해야 한다. 다만, C++의 경우 C++20 개정판부터 모듈 개념이 도입될 예정이므로 이러한 단점은 점차 시간이 지남에 따라 어느 정도 극복될 것이다. 다른 모듈에서 정의된 매크로는 사용하지 못하기 때문에, 매크로로 떡칠된 코드는 다 뜯어 고쳐야 할 수도 있다. 이 때문에 수많은 기존 프로젝트는 여전히 헤더를 사용할 것으로 보이며, C와의 호환성을 위해 헤더 참조 구조는 표준에서 없어지지 않을 것이다.

또한 C++98 초과의 최신판은 메인을 제외한 함수에 반환값의 자료명을 꼭 명시해 주어야 한다.

이외에도 C에서만 지원되는 문법이 몇 있다. 그 예로, K&R 스타일의 함수 정의 구문[11]의 지원과 C99에서 추가된 지정 초기자(Designated Initializers)[12] 의 일부 형태 등이 있다. C++20 표준에서 C99의 지정 초기자와 굳이 차이를 둔 이유를 설명한 예시는 다음과 같다.
#!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)

iostream이나 fstream 등 입출력 스트림이 C의 stdio/file등 입출력 함수보다 상당히 느리다. 만약 프로그램내에서 std::cin, std::cout등 C++ 스트림[13] 계열만 사용하고 stdin, stdout등 C스트림[14]을 사용하지 않는다면, C++스트림과 C스트림간의 동기화를 정지시켜 iostream에 비약적인 속도 향상을 가져올 수 있다[15].
#!syntax cpp
int main()
{    
    std::ios_base::sync_with_stdio(false);
    //...
}

2.2. C++에서의 객체지향 프로그래밍

C++는 객체 지향 프로그래밍을 지원하지만, C++의 객체지향은 다른 객체지향 언어에서와는 성격이 좀 다르다. 대부분의 객체지향 언어에서는 많은 부분을 런타임에 처리하며 메모리를 자동으로 관리하는 반면, C++에서는 최대한 많은 것을 컴파일 타임에 처리하는 것을 지향하며 메모리 등을 프로그래머가 직접 관리하게 하기 때문에 전반적인 클래스 디자인 자체가 상당한 차이를 보이게 된다. Java 등의 다른 객체지향 언어에서와 같은 방식으로 C++ 클래스를 디자인하면 거의 틀림없이 컴파일이 제대로 안 되거나 메모리 문제가 발생한다. 특히 가비지 컬렉션을 지원하는 Java로 입문하여 C++로 갈아타는 테크를 탄 학생이라면 처음에는 메모리가 줄줄 새는 프로그램을 만들게 될 것이다. 반면 C++ 스타일을 숙지하여 다른 객체지향 언어에서 프로그래밍을 하는 경우 특별히 안 될 것은 없지만, 해당 언어의 스타일로 작성한 원어민 코드에 비해 시간이 많이 걸리고 너저분해 보이며 클래스 구조가 경직되어 수정하기 어려워진다. 소멸자가 호출되지 않는 등의 차이점은 있지만 심각한 문제가 되는 경우는 많지 않다.

대표적인 예가 함수 내부에서 객체를 동적으로 생성해서 그 객체의 raw 포인터를 반환하는 것. C++에서는 이는 메모리 문제를 일으키기 딱 맞는 방식이라서 클래스 디자인 단계에서 이런 종류의 함수가 필요없게 하기 위한 고려가 필요하다. 거의 대부분의 프로그램은 실행되고 있는 코드의 각 분기점에 대한 변수를 스택 영역에 저장하는데, 한 함수의 실행이 완료되면 그 함수의 스택 영역 변수를 위한 메모리는 모두 삭제된다. 그리고 이 변수는 쓰레기 데이터로 남아있다가 다른 함수를 호출할 때 덮어씌워진다. 해당 함수가 끝나더라도 공유 자원과 같은 특정 데이터를 유지하려면 힙 영역에 할당해야 한다. 그러나 가비지 컬렉션이 자동으로 되는 언어에서는 매우 흔히 사용되는 방법이다. 이런 차이 때문에, Java, C# 등의 언어를 먼저 접한 프로그래머들은 C++의 객체지향이 짝퉁이라며 싫어하기도 한다. 하지만 객체지향 프로그래밍 언어임을 전면에 내세우는 Java나 C#과 달리, 객체지향 패러다임 지원하는 멀티패러다임 언어 C++ 입장에서는 얼마나 순수하게 객체지향의 이상을 잘 따르는가보다 C++가 제공하는 다른 패러다임까지 아우르는 내적 일관성이 더 중요하므로 해당 언어들과 C++를 동일선상에 놓고 비교할 수는 없다.

이러한 차이가 생기는 것은 대부분의 객체지향 언어는 직접적으로 Smalltalk의 영향을 받은 반면, C++는 Smalltalk보다 먼저 객체지향의 초보적인 개념을 제시한 시뮬레이션 전용 언어인 Simula에서 직접 영향을 받았기 때문이다. 이는 C 프로그램과의 호환성을 고려한 결과이기도 하지만, 기본적으로 C++에서 프로그램의 성능을 희생시키지 않기 위해서였다. Smalltalk의 경우 당시의 기술적 한계도 있고 해서 C보다 대체로 수십 배 정도 느렸고, 이는 C++에서 지향하는 결과가 아니었기 때문이다.

다만 이런 제한이 생기는 건 오래된 스타일의 C++ 코드에서나 그런 것이고, Modern C++에서는 스마트 포인터를 사용하거나 이를 move[16]하는 방식으로 그들과 완전히 같은 문법이 구현 가능하기 때문에 더 이상 해당이 없는 사항이다. 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을 사용하면 여러 소유권자가 하나의 객체를 공유하면서도 순환 참조 문제를 해결할 수 있다.

2.3. C++에서의 일반화 프로그래밍

C++에서는 템플릿을 이용한 일반화 프로그래밍(Generic Programming)이 매우 폭넓게 사용된다. 특히 C++11을 시작으로 하는 모던 C++는 일반화 프로그래밍을 빼고 이야기하는 것이 불가능하다. 당장 매우 널리 사용되는 std::string만해도 실제로 들여다보면 std::basic_string<char>와 같은 형태의 클래스 템플릿의 특수화에 지나지 않는다. 또, C와는 달리 배열 생성에 포인터 혹은 기본 배열 타입 대신 std::vector를 기본으로 사용하라고 가르치는데, 이 std::vector 또한 저장될 변수의 타입을 템플릿 파라미터로 받아들이는 클래스 템플릿이다. (예를 들어 std::vector<int>) 정적 배열이 꼭 필요할 경우 std::array를 쓴다.

일반화 프로그래밍의 결과물으로는 C++ 표준 라이브러리의 일부분으로 포함된 컨테이너, STL 같은 것들이 있으므로, C++ 표준 라이브러리를 사용하는 순간 일반화 프로그래밍의 도움을 받는 것이다. 따라서 '나는 C++를 사용하지만 템플릿을 이용한 일반화 프로그래밍은 어려우니까 패스하겠다'는 말은 애초에 성립하지 않는다.

중급 이하 개발자는 이런 결과물을 일상적으로 쉽게 가져다 쓰면 되고, 직접 만들어 쓸 필요는 없다. 이는 중급 개발자 정도로는 템플릿을 사용한 일반화 프로그래밍 기법을 정확하게 적용하는 것이 무척 까다롭고, 디버깅할 때 이해 불가능한 컴파일러 메세지를 받게 되는 경우가 많아서 발견된 오류를 수정하기도 어렵기 때문이다.

이런 이유로 C++ 학습 초반에 템플릿 프로그래밍을 직접 하는 것을 피하고 기껏해야 컨테이너 클래스만 사용하는 습관이 들다 보니, C++는 객체지향 언어이고 STL이라는 템플릿 라이브러리를 덤으로 쓸 수 있는 정도라는 오해가 널리 퍼진 것이라 생각된다. 하지만, 특수 목적의 컨테이너를 설계하거나 범용 라이브러리를 설계하는 수준의 고급 개발자가 되려면 템플릿을 사용한 일반화 프로그래밍을 해야 한다.

Java C#에서 찾아볼 수 있는 Generics가 지금 설명한 C++ 일반화 프로그래밍의 아주 제한된 형태의 적용례에 해당한다.

2.3.1. 템플릿 메타 프로그래밍

TMP, Template Meta Programming

템플릿 문법을 응용해서 컴파일 시점에 자동 코드 생성 및 계산 같은 것을 한다는 개념이다. 영문위키백과 배우기 어렵고 알아보기도 힘들고, 오류 찾기도 힘들다. 당연히 C++의 템플릿을 모를 경우에는 아예 이해할 수가 없는 개념이기도 하다. 하지만 알아두면 은근 써먹을 데가 많다.

템플릿을 남용하면 바이너리 크기가 n배로 증가하는 참사가 발생할 수도 있다. 이럴 경우 명령어 캐시의 실패 확률이 늘어나서 오히려 성능이 하락할 수도 있다. 특히 이런 식으로 템플릿을 구현시 Windows 환경에서 문제가 생기는 경우가 있는데 Windows가 사용하는 PE-COFF 포맷은 PE32, PE64관계 없이 한개의 섹션에 65279개가 넘어가는 심볼을 기록할 수 없기 때문이다. 하지만 제대로 구현하면 바이너리 크기에서 손해를 보지 않으면서도 컴파일 시점에 다양한 처리나 계산을 할 수 있기에 라이브러리 구현에 종종 이용된다.

그리고 처음에는 너무 병맛이라 입에서 욕이 튀어나올 수 있다. 악용에 가까운 테크닉으로는 다음이 있다.
  • SFINAE(Substitution Failure Is Not An Error): 함수를 오버로딩 하는데 있어 조건에 따라 일부러 문법적으로 틀린 템플릿의 구현 코드를 발생시켜 틀리지 않은 문법이 되는 특정 오버로드만 선택되게 만드는 테크닉이다. C++ 문법의 틈새에 존재하는 문법 오류이지만 컴파일 에러가 아닌 상황을 적극 활용하는 것이다. 직역하면 '대입 실패가 에러라는 뜻은 아니다'라는 뜻이다.
  • Expression Templates: 디자인 패턴의 일종인 Proxy pattern 기반의 Lazy evaluation이 적용되는 효율적인 계산 코드를 컴파일 시점에 자동으로 생성하는 테크닉. 일반적으로 연산 도중의 임시 객체 생성 문제를 이 기법을 통해 해결하는 경우가 있다. RVO(Return Value Optimization)를 감안하더라도 C++ 특성상 연산자를 활용하는 과정에서, 직접적인 연산을 시도하면 임시 객체의 생성을 완전히 막을 수는 없기 때문이다.

이 개념은 C++ 템플릿이 코드를 컴파일 시점에 확장하는 방식이라는 것과 비타입 인수도 줄 수 있다는 것을 이용한다는 점을 이용하는 것으로 다른 언어에서는 비슷한 것도 찾기 힘들다. 전술한대로 Java C#에 있는 제네릭은 TMP가 아닌 일반화 프로그래밍(Generic Programming)의 개념이다.

같은 템플릿 문법을 사용하지만 앞 문단에서 강조한 일반화 프로그래밍과는 엄연히 목표가 다르다. 일반화 프로그래밍이 코드의 범용성 증대를 목표로 한다면 템플릿 메타 프로그래밍은 컴파일러를 고문해 어떻게든 실행시간의 이득을 얻은 것을 목표로 한다. 근본적으로 C++의 템플릿 문법이 compile-time turing complete하기 때문에 이런 일이 가능한 것이다. C++ 안에 컴파일 타임 전용의 또 다른 언어가 숨어있는 것과 같은 상황이다.

C++17의 if constexpr와 C++20의 concept의 등장으로 이전보다 상대적으로 알아보기 쉬운 TMP 코딩이 가능해졌고, 컴파일 오류가 발생했을 때의 에러 메세지또한 좀 더 이해하기 쉽고 간결하게 표시되게 되었다.

예를 들어, 피보나치 수열을 템플릿을 이용하여 컴파일 시점에 계산하는 C++11 코드는 다음과 같이 쓸 수 있다.
#!syntax cpp
template <size_t N>
struct Fibonacci
{
	static constexpr size_t Value = Fibonacci<N - 1>::Value + Fibonacci<N - 2>::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을 활용하면 더 간결하게 쓸 수 있다.[17]
#!syntax cpp

constexpr size_t fibonacci(size_t n)
{
	return (n < 2) ? 1 : fibonacci(n - 1) + fibonacci(n - 2);
}

int arr[fibonacci(7)];

GCC와 같은 컴파일러는 최적화 옵션을 넣지 않을 경우 constexpr 함수를 컴파일 타임에 계산하지 않는데, 아래와 같이 템플릿 메타 프로그래밍을 섞어서 사용하면 확실히 컴파일 타임에 계산된다.
#!syntax cpp
template<size_t N>
constexpr size_t Fibonacci()
{
	if constexpr (N < 2) return N;
	else return Fibonacci<N - 1>() + Fibonacci<N - 2>();
}

int arr[Fibonacci<7>()];

여기서 arr은 길이가 13인 배열이 된다.

C++의 TMP의 용도는 멀티메소드, 펑터, 트레잇, 타입리스트, CRTP, variant visitor, mixin 등 수많은 풍부한 디자인 패턴은 C++에서 TMP 아니면 시도조차 할 수 없다. C++17 기준으로 쓰여진 C++ 템플릿 기본서 는 그 쪽수가 800쪽을 넘는다.

실무에서 가장 많이 쓰이는 TMP 테크닉 중 하나로 CRTP (Curiously Recursive Template Pattern) 패턴이 있다. 이는 Java나 C#등의 언어에서 가상함수 기반 클래스 상속을 구현할 시 가상함수 호출에 따른 오버헤드를 막고자 고안되었다. 이는 다음과 같이 쓴다.

#!syntax cpp
template <typename Derived>
class CRTPBase {
     // ...
};

template <typename T>
class CRTPDerived1 : public CRTPBase<CRTPDerived1<T>> {
     // ...
};

template <typename T1, typename T2>
class CRTPDerived2 : public CRTPBase<CRTPDerived2<T1, T2>> {
     // ...
};



파생 클래스를 템플릿 인자로 기반 클래스에 넘겨줌으로써, 기반 클래스는 멤버 함수의 동작을 각 파생 클래스에 대해 특수화할 수 있다. 템플릿 인자는 컴파일 타임에 알 수 있는 내용이므로, 프로그램 내에서 기반 클래스가 어떤 파생 클래스로 인스턴스화되는지 컴파일 타임에 알 수 있다. 따라서 런타임 가상 함수 디스패치 및 그에 따른 오버헤드가 필요없다! 이는 멤버 함수 호출이 잦은데 상속은 구현하고 싶은 경우에 성능상으로 엄청난 차이를 낸다.

2.4. 성능

C는 유닉스라는 OS를 만들기 위해 어셈블리 대체용으로 만들어진 언어라, 머신 컨트롤의 저수준 작업이 주된 임무 중 하나였다. 그리고, C++은 그 이름에서 보이듯이 사실상 C를 대체하기 위한 언어였기 때문에 C가 제공하던 머신 컨트롤의 저수준 작업들을 포기하지 않고 그대로 지원한다.

거기다 그 위에 여러 가지 추상적인 고수준 기능들을 집어넣어 추상적인 시스템 위에서만 노는 게 아니라, 저수준의 기계 제어까지 가능한 추상화라는 요상한 철학을 지지한다. 그래서 Java 같은 언어들이 객체 지향 프로그래밍을 구현하면서 동적 바인딩(C++의 가상 함수)을 디폴트로 쓰고 쓰레기 수집을 지원할 때 C++은 정적 바인딩을 디폴트로 하고 수동 메모리 관리를 유지한 것이다. 일반적으로 추상화 수준이 높은 언어일수록 프로그래머 머리로 해야할 구체적인 것들을 컴퓨터가 대신 해주고, 이것이 항상 최적화된 방식은 아니기 때문에 프로그램의 실행 속도가 상대적으로 느리게 마련이지만, C++ 프로그램은 위와 같은 이유로 성능 하락이 거의 없다.

추상화의 가장 큰 이유와 장점은 그것을 추상화시킴으로서 그 아래 감춰진 디테일한 부분을 신경쓸 필요가 없게 만드는 것이다. 따라서 저수준 제어와 고수준 추상화의 두 가지 개념은 서로 완전히 충돌하는 부분이다. C++는 저수준 제어를 포기하지 않았기 때문에 프로그래머는 C++로 저수준 작업을 할 때는 디테일한 부분까지 신경써야만 한다. 반대로 C++에서 고수준 기능을 사용하려면 프로그래머가 저런 부분을 감안하여 C++ 방식으로 변형되어 적용된 코딩 문화(RAII[18] 등)를 알고 있어야 한다.

템플릿이 사용된 모든 타입에 대해 컴파일 시간에 확장하도록 설계된 이유 또한 실행 성능을 포기하지 않기 위해서이다. C++과 비슷한 정도의 기능을 가진 언어 중에서 C++만큼 빠른 실행 성능을 내는 언어 구현은 흔치 않다. 대신 프로그래머가 언어의 이상한 부분까지 신경쓰지 않으면 안 되는 디자인이 되어 제대로 쓰는 게 굉장히 어려운 언어가 되고 말았다. 뭔가 딜레마 같지만 C++과 비슷한 정도의 고수준 기능을 제공하는 언어는 절대로 C++만큼 복잡하지 않다. 덤으로, 템플릿 같은 경우엔 실행 성능을 포기하지 않는 대신 컴파일 시간을 심각하게 포기했다. 심지어 디버깅도 힘들다. 다만 디버그 문제는 자기가 원하는 템플릿에 맞춰 중단점을 거는 게 가능해지는 등 많이 개선된 편이다.

애초에 C 언어의 기능을 모두 포함했던 이유는 생산성과 함께 C를 대체하겠다는 두마리의 토끼가 목적이었기 때문이다. 그러나, OS를 만드는 유닉스 커뮤니티들이 C++로의 전환을 거부해서 한마리 토끼는 놓쳤지만 그럼에도 Java라는 강력한 언어가 등장하기 전까지는 C에 비해 압도적인 생산성으로 어플리케이션 소프트웨어 쪽에서 순식간에 대세언어가 되었으나 Java의 등장과 각종 스크립트 언어들의 부상, 그리고 비주얼 베이직에서 Python으로 이어지는 하이퍼 고 생산성 언어들의 등쌀에 현재는 포지션이 좀 애매해진 경향이 있다. 애초에 C의 기능을 전부 포함했던게 공짜로 된 것이 아니라 그만큼 복잡해지고, 컴파일 속도까지 희생하는 역효과도 포함하고 있었고, 덕분에 Java나 C# 등의 기타 고생산성 언어들에 비해 생산성은 떨어지면서 정작 노리고 있던 C의 대체도 날아간 상황.

현재는 클라이언트 쪽에서는 퍼포먼스가 엄청나게 중요하면서 동시에 개발속도도 크리티컬한 게임[19], 포토샵, 웹 브라우저 등의 데스크탑 어플리케이션, 업무 연속성이 중요한 금융권 IT망 및 금융공학에서 주로 사용된다. 사실 그 외에도 퍼포먼스를 중시하는 경우 여전히 많이 사용된다. 구글에서도 상당히 많이 사용하는 언어이다. # 이미 구글에서 2009년에 개발한 컴파일 기반 언어인 Go가 나와 있었는데도 텐서플로우가 C++로 개발되었다는 점이 하나의 예시이다.

2.5. 장점

2.5.1. 많은 시스템에서 지원하는 언어

다른 언어에 밀려 점유율이 조금씩 줄어들고는 있지만, 오래전부터 사용되어왔기 때문에 많은 시스템에서 지원하며, C++로 계속 유지되고 있는 프로젝트가 많다. TIOBE에는 2021년 12월 기준 4위를 차지하고 있다. PYPL의 경우 2021년 12월 기준 5위를 차지하고 있다.

비아르네 스트로우스트루프에 따르면[20] C++20에 도달한 지금의 C++를 한 마디로 요약하면, An invisible foundation of "everything"(모든 것의 보이지 않는 기반)이라고 한다. C++가 드라이버와 같은 시스템의 핵심 구성 요소를 작성하고 유지하는데 사용됨으로써 보이지는 않지만 분명히 존재한다는 것이다.[21]

2.5.2. 성능

C++은 시스템 프로그래밍 언어이기 때문에 zero-cost abstraction, 즉 "네가 쓰지 않는 것에 대해서는 성능 하락을 주지 않는다"라는 것을 언어의 설계 철학으로 하고 있다. 또한, 런타임 성능을 쥐어짜내기 위해 인라이닝, 컴파일 타임 프로그래밍 관련 수많은 기능을 도입한 언어이기 때문에 같이 비교되는 시스템 프로그래밍 언어들인 C, Rust에 비해 '프로그래머의 기량이 뒷받침된다면' 더 빠르게 동작하는 코드를 짤 수 있다. 벤치마크 결과[22]

2.5.3. 수많은 기능

C++은 절차지향, 객체지향, 일반화 프로그래밍 등 프로그래밍 언어에서 제공할 수 있는 대다수 패러다임을 지원하는 언어이다. 같이 비교되는 C의 경우 객체지향이나 일반화 프로그래밍을 언어 차원에서 지원하지 않기 때문에 이를 흉내내려면 알아보기도 힘든 매크로 떡칠을 동원해야 하는데, 이를 언어 차원에서 성능 하락 없이 쓸 수 있다는 건 C에 비해 엄청난 메리트라고 할 수 있다. 성능에 목숨을 걸면서 프로그램의 복잡도가 높아지는 텐서플로우, 파이토치, 행렬 라이브러리, 금융권의 트레이딩 시스템 등은 전부 C 대신 C++을 사용하는데, 이것이 괜히 그런 것이 아니다.

2.6. 단점

2.6.1. 난이도

파일:learnC++.jpg

다른 언어에 비교해 매우 복잡하고 배우기가 어렵다. 문법의 방대함은 타의 추종을 불허하기 때문에,[23] 모든 문법적 요소를 이해하겠다는 생각은 애초부터 버리는게 좋다. 그럴 필요도 없거니와, 불가능하다. c++로 실무 업무를 보는 개발자들도 절대로 c++ 문법 요소 전부를 가져다 쓰지 않고 필요한 부분만 참고해가며 사용한다. 특히 C++11의 대표적인 기능 중 하나인 우측값 레퍼런스는 초보자가 이해하기 어렵다. 직접 보자.

더 큰 문제점은, C++의 경우 기능이 너무 많은데 그 중 실제로 잘 배울 필요가 있는 기능은 그렇게 많진 않고, 나머지는 라이브러리 제작자가 아닌 이상 배울 필요가 없는 기능과, 쓰면 오히려 독이 되는 (주로 C언어 또는 오래된 레가시 C++ 코드와의 호환성 때문에 억지로 표준에 유지시켜 놓은) 기능들이다. 저질 학습 자료(또는 철 지난 학습 자료)들이 지나치게 많기 때문에 초보자 입장에서는 어떤 것이 좋은 습관이고 어떤 것이 좋지 않은 습관인지 익히는 것이 어렵다.

C++을 배우려는 초보자가 인터넷에 문의를 해보면 보통 초보자용 1개, 중급자용 1개, STL 1개 등등등 해서 여러 개 읽으라는 답변들이 많이 달리며, 개중에는 1000쪽이 넘어가는 책들도 있다.[24] 물론 반드시 1000쪽짜리 책으로 공부해야 한다는 건 아니지만 C++ 교재들은 대체로 다른 언어 교재에 비해 두꺼운 편이다.
2.6.1.1. 메모리 관리
C++는 C언어와 마찬가지로 프로그래머가 직접 메모리를 관리하도록 함으로써 자유도가 높지만 그만큼 실수하기 쉽고 설계가 어렵다는 단점이 있다. 다른 프로그래밍 언어와 구별되는 가장 큰 차이점이다.

프로그래머들의 평균적인 기량이 뛰어나더라도 개발팀의 사람이 많아진다면, 사람은 가끔이라도 반드시 실수를 하게 되므로, 프로그램 내에서 메모리 문제가 언젠가는 터질 위험이 있다.[25] Rust처럼 언어 차원에서 컴파일 타임에 오브젝트의 메모리 소유권을 검사하고 강제하는 기능이 없기 때문에, 이 부분은 C++으로 프로그래밍할 때 언제나 염두에 둬야 하는 폭탄 같은 위험성이라고 볼 수 있다.

하지만 std::unique_ptr과 이동 시맨틱, RAII 등의 기법을 잘 공부하고 활용하기만 한다면, 이러한 위험성을 방지할 수 있다.
2.6.1.2. 갈수록 늘어나는 기능
C++11이 발표된 2011년을 기점으로 매 3년마다 개정된 표준을 발표하는 것이 관례화 되면서, 기능이 추가되고 성능이 개선되었지만 그만큼 배워야 할 것은 더욱 늘어나게 되었다. Java C언어에 비해 C++는 변화의 폭이 제법 큰 편이기 때문이다.

C++11 이후 점차 추가되어가는 표준 라이브러리 기능과 문법들을 사용한 코드를 보면 C언어 기반 언어라고는 생각이 들지 않을 정도로 차이가 커지고 있다. 3년마다 개정이 이루어지면서 더더욱 심화되고 있는데, 기존의 C++의 스타일과 많이 달라지고 있다. C++98을 잘 알고 있다는 가정 하에, 모던 C++의 새로운 기능을 배우고 싶다면 이들을 자세히 구글링하여 찾고 부족한 내용은 C++ 표준 라이브러리를 정리한 사이트에서 알아보는 것이, 가장 최신의 C++ 기능들을 빠르게 익힐 수 있는 방법이다.

2.6.2. 애매한 위치

리눅스 진영의 주축인 리누스 토르발스가 메일링 리스트를 통해 언급한 2007년도 비판이 자주 인용된다. 리누스 토르발스에 의하면 C++는 어중간한 지점에 위치한 언어이며 운영체제 개발에 있어 아무런 도움이 되지 않았다고 언급했다. 실제로 C++로 리눅스를 재작성하려고 했다가 다시 되돌린 적이 있다. 프로토타이핑 용도나 단순한 GUI 프로그래밍 용도로 사용하기에는 C++가 일을 충분히 쉽게 하도록 도와주는 친절한 언어가 아니고, 반대로 단순하고 직접적인 구조의 프로그래밍을 장려하는 C언어처럼 날씬한 운영 체제 프로그래밍용 언어도 아니라는 것이다. #

리누스 토르발스의 발언을 인용한 C++ 비판에 대한 비아르네 스트로우스트루프의 가장 최신 반응은 '그것은 기껏해야 고대의 C++에 대한 비판일 뿐 모던 C++에 적용되는 얘기가 아님.'[26]으로 요약할 수 있다.
"... 사람들은 매우 자주 C++를 잘못 묘사하곤 한다. 사람들은 1984년 근처의 C++를 바라보고 비판을 하곤 하거나, 25년 전[27]의 비판을 발굴해내서 무슨 복음말씀처럼 여기곤 한다. 심지어 나는 오늘 아침에도 리누스가 1990년대 초에 C++에 대해 언급한 코멘트를 레퍼런스로 하는 것을 보았다. 그 당시의 g++ 컴파일러라면 나도 싫다. 하지만 그건 수십년 전이고, 세상은 25년 전과는 많이 달라졌다."

실제로 C++20 이후 코루틴과 모듈 및 컨셉트와 같은 기능이 표준안에 추가되면서 아예 다른 언어처럼 보일 정도로 많이 바뀌고 개선되었다. 다만, 2021년 12월 기준 C++를 뒤바꿀 새로운 기능이 나왔음에도 이를 지원하는 라이브러리나 사용하는 프로젝트가 다른 언어들에 비해 거의 없고 활성화되지도 않았다. 게다가 C++를 사용하는 기업들은 안정적이고 오래된 표준안을 선호하므로, 새로운 C++ 문법과 기능을 실무에서 사용하게 되기까지 많은 시간이 걸릴 것이다.
2.6.2.1. C언어와의 호환성
C언어와 호환된다는 특징은 그 자체로 장점과 단점을 모두 갖게 된다. 장점은 높은 호환성과 더불어 C언어 프로젝트와 섞어서 사용하기 쉽다는 것이고 단점은 C언어에 문법을 추가하다보니 C언어의 단점을 그대로 가져오게 되며 다른 언어에 비해 비교적 깔끔하지 못하다는 것이다.

C언어의 단점에 대한 대표적인 예로는 매크로가 있는데, C/C++의 매크로는 문자열을 단순 치환하기 때문에 코드 문맥이나 흐름에 따라 디버그하기도 어려운 난해한 문제를 발생시키는 경우가 많다. 초보자들에게 매크로를 함부로 쓰지 말라는 이유가 바로 이 때문이다. #

C언어에서 모든 프로젝트에 빠짐없이 사용되는 #include 전처리기 구문 또한 C/C++의 단점이다. 수많은 프로젝트에서 이러한 구문을 사용하고 있는데, 특히 단순 치환하는 매크로 때문에 코드 간 의존성이 발생하여 미리 컴파일할 수 없으며 해당하는 헤더 파일을 컴파일 할 때마다 일일이 포함해야 한다. 그 결과 C/C++는 컴파일이 가장 느린 언어가 되어버렸다. 이러한 문제를 해결하기 위해 C++20에 모듈이 도입되었고, MSVC에서는 #include 전처리기 구문을 사용하더라도 모듈로써 동작하도록 설정할 수 있다. 하지만 이는 모든 C++ 컴파일러에서 지원하는게 아닐 뿐더러, 모듈을 지원하거나 사용하는 라이브러리가 아직은 전무하다. 아니면 이 상황에서 MSVC, GCC, Clang, ICC와 같은 메이저 컴파일러들은 헤더를 프리컴파일하고 사용하는 확장 기능들을 제공하고 있으므로 이를 사용하는 것이 최선이다.
2.6.2.2. 템플릿의 난해성
이 문서는
이 문단은
토론을 통해 C++TMP에 대한 비판을 서술하되 개발자가 의도한 상황에서 발생하지는 않으며 부수적 차원에서 발생하는 문제임을 명시하여 서술한다(으)로 합의되었습니다. 합의된 부분을 토론 없이 수정할 시 제재될 수 있습니다.
아래 토론들로 합의된 편집방침이 적용됩니다. 합의된 부분을 토론 없이 수정할 시 제재될 수 있습니다.
[ 내용 펼치기 · 접기 ]
||<table width=100%><table bordercolor=#ffffff,#1f2023><bgcolor=#ffffff,#1f2023><(> 토론 - C++TMP에 대한 비판을 서술하되 개발자가 의도한 상황에서 발생하지는 않으며 부수적 차원에서 발생하는 문제임을 명시하여 서술한다
토론 - 합의사항2
토론 - 합의사항3
토론 - 합의사항4
토론 - 합의사항5
토론 - 합의사항6
토론 - 합의사항7
토론 - 합의사항8
토론 - 합의사항9
토론 - 합의사항10
토론 - 합의사항11
토론 - 합의사항12
토론 - 합의사항13
토론 - 합의사항14
토론 - 합의사항15
토론 - 합의사항16
토론 - 합의사항17
토론 - 합의사항18
토론 - 합의사항19
토론 - 합의사항20
토론 - 합의사항21
토론 - 합의사항22
토론 - 합의사항23
토론 - 합의사항24
토론 - 합의사항25
토론 - 합의사항26
토론 - 합의사항27
토론 - 합의사항28
토론 - 합의사항29
토론 - 합의사항30
토론 - 합의사항31
토론 - 합의사항32
토론 - 합의사항33
토론 - 합의사항34
토론 - 합의사항35
토론 - 합의사항36
토론 - 합의사항37
토론 - 합의사항38
토론 - 합의사항39
토론 - 합의사항40
토론 - 합의사항41
토론 - 합의사항42
토론 - 합의사항43
토론 - 합의사항44
토론 - 합의사항45
토론 - 합의사항46
토론 - 합의사항47
토론 - 합의사항48
토론 - 합의사항49
토론 - 합의사항50
||


상대적으로 느슨한 언어 설계에 의해 SFINAE와 같은 사실상 꼼수에 가까운 기법들이 사용되고 그 중 TMP 또한 이에 포함된다. 여기서 말하는 메타 프로그래밍은 컴파일 타임에 실행되는 프로그램이라는 넓은 의미를 가진다. 템플릿은 원래 일반화 프로그래밍을 위해 등장하였으나, 템플릿 타입 체크 등의 몇 가지 케이스들을 보완하기 위해 여러 기능들이 추가되기 시작했다. 이러한 기능들은 SFINAE[28]와 같은 트릭에 사용되다가, 어느 때부터 C++ 템플릿을 활용하여 튜링 완전한 메타 언어로써 연산을 컴파일 타임에 수행할 수 있음을 발견했다. 이러한 흐름에서 등장한 템플릿 메타 프로그래밍은 C++의 강력한 기능이지만, 원론적으로는 언어 표준 명세에서 지원하는 것이 아니였다보니 이를 이해하거나 작성하기 어려우며 컴파일도 매우 오래 걸린다. # # # SFINAE가 수행했던 타입 체크는 C++20에서 concept를 도입하여 템플릿에 명시적 조건을 추가할 수 있게 됨으로써 의미가 없게 되었다. [29]

2.6.3. 성능 저하 요소

C++의 고성능에 대한 철학은 흔히 "don't pay for what you don't use", 즉 성능에 군더더기 없는 기능으로 대표된다. 하지만 두 가지 언어 요소가 이를 정면으로 어기는 기능으로 여겨지는데, 하나는 RTTI(Run-Time Type Information)이고, 다른 하나가 바로 예외(Exception) 이다.
2.6.3.1. RTTI
RTTI(Run-Time Type Information)는 컴파일 타임이 아니라 런타임에 객체의 타입이 결정되는 C++의 매커니즘이다. 프로그램이 실행될 때, 객체에 대한 정보를 저장하고 확인해야 하기 때문에 오버헤드가 발생한다. 다음 문법이 대표적인 예이다.
- dynamic_cast
- virtual

dynamic_cast의 경우 상속 트리 전체를 휘젓고 다녀야 한다는 점 역시 큰 성능 문제로 꼽힌다. 하지만 이에 의존하는 언어의 기능이 거의 없기 때문에 대부분의 상황에서 성능 패널티를 피할 수 있다. 메이저 컴파일러에서 아예 기능을 제외하는 옵션을 제공하는 건 덤이다.

virtual의 경우 50% 정도의 성능 하락이 있을 수 있지만, 현대 컴퓨터에서는 분기 예측을 통해 평균 5~10% 정도의 성능 하락을 보여준다. 그리고 이러한 성능 하락은 어디까지나 함수 호출 비용으로만 계산한 것이기 때문에 특수한 상황이 아니라면 고민하지 않아도 된다. 많은 소프트웨어가 이 문법을 자주 사용한다.[30]

현재 RTTI는 유행이 많이 지난 기능이며, 최신 C++ 프로그래밍에서는 잘 사용하지 않는다. 동적 타이핑은 꼭 필요할 때만 사용하는 것이 대세이며, 이외에는 다형성을 구현하기 위해서는 CRTP + type erasure 등의 기법을 주로 사용하며, 이는 성능 패널티가 없다.
2.6.3.2. 예외
예외도 마찬가지로, 설계상 예외 핸들러를 찾기 위해 (RTTI와 동일한 맥락으로) 던져지는 예외의 타입별로 타입 캐스팅을 해야 하기 때문에 런타임 타입 정보가 필요하다.

따라서 예외를 던질 때마다 적지 않은 성능 패널티가 부과된다. 심지어 예전에는 try 블럭을 설치하는 것과, 소멸자가 있는 오브젝트를 정의하는 것만으로도 프로그램 성능이 크게 하락했었다. 최신 컴파일러는 zero-cost exception[31]을 사용하기 때문에 성능 하락이 거의 없다. 현대 컴파일러에서 if문 처리는 분기 예측에 따라 1~20 cpu cycle를 소모하고, 예외 처리는 5000~10000 cpu cycle를 소모한다 #는 점을 고려하여, 불필요한 예외는 사용하지 말아야 하며 성능이 중요한 부분에서만 예외 처리를 사용하지 않는 것을 추천한다.[32] #

RTTI와 달리 C++의 표준 라이브러리를 사용하고 에러 처리를 동반하는 한 예외는 현실적으로 피할 수 없다. C++에서 객체의 생성이나 복사를 실패시킬 수 있는 문법적 기능은 예외가 유일하며[33] 표준 라이브러리는 이를 적극적으로 활용하기 때문이다.[34]

에러 처리 수단은 수많은 프로그래밍 언어의 핵심 매커니즘이다. 하지만 이런 상당한 단점이 있게 되자, 대규모 프로젝트에서는 성능 상의 이유로 예외의 사용을 금지하게 되고[35], 오히려 C에서 흔히 쓰는 에러코드 등의 매커니즘으로 회귀하게 되었다. 심지어 에러 처리를 일관적으로 예외로 하던 표준마저도 어느 정도 스탠스를 바꾸어, C++17의 filesystem 라이브러리의 경우 예외 처리와 에러코드용 인터페이스를 혼용해서 제공해버릴 정도로 C++의 예외 처리는 처리 비용이 상당하다.

이렇게 성능 상의 문제로 예외 처리를 기피하는 상황이 발생하자, 아예 에러코드 방식을 확장시킨 Boost.Outcome 혹은 std::expected[36] 등으로 발전하려고 하고 있다. 타입 캐스팅을 포기하고 에러코드와 예외의 장점을 섞은 표준안이 제안되고 있으므로 #P0709 언젠간 개선되리라는 희망을 가질 수는 있겠지만, 아직도 많은 논의가 계속되고 있기 때문에 # 당분간은 적용이 요원하다.

3. 역사

3.1. 클래식 C++

3.1.1. C with Classes (1979년)

1979년에 비아르네 스트로우스트루프가 속도가 느리지만 객체 지향적인 언어인 Simula의 컨셉을 C언어에 접목시키면서 C with Classes라는 이름으로 만들었다.

3.1.2. C++ (1983년)

1983년에 가상 함수, 연산자 오버로딩, 참조, const, new/delete 연산자 등을 포함한 새로운 기능들이 추가되면서 언어 이름도 C with Classes에서 C++로 변경되었다. 이 시점에서 이미 클래식 C++의 틀은 완전히 잡혔다고 할 수 있으며, 이때까지는 거의 스트로우스트루프가 언어 개발을 전담하다시피 했다.

1985년에 본인이 쓴 책인 The C++ Programming Language를 통해 C++라는 언어를 정의하여 레퍼런스 컴파일러를 배포하는 식으로 개발되었다. 한편, 컴파일러 쪽에서는 백엔드는 여전히 C 컴파일러이지만 프론트엔드만 Cfront라는 이름으로 새로 개발되었다.

3.1.3. C++ 2.0 (1989년)

1989년에 다중 상속, 추상 클래스, static 멤버 함수, const 멤버 함수 및 protected 멤버 등의 새로운 기능들이 추가된 C++ 2.0이 발표되었으며, 컴파일러의 프론트엔드도 Cfront 2.0으로 업그레이드되었다.

1990년에 C++ 2.0에 추가된 기능 외에 템플릿, 예외, 네임스페이스, new casts 및 bool 타입 등의 새로운 기능들이 추가된 '주석이 달린 C++ 레퍼런스 메뉴얼'이 발표되었고 1991년에 컴파일러 프론트엔드가 Cfront 3.0으로 업그레이드되었으며, The C++ Programming Language의 첫 번째 개정판이 발행되었다.

3.1.4. C++98 (1998년)

1998년에 최종 승인된 C++의 첫 공식 표준안으로, C++ 2.0 이후 C++가 안정적으로 정착하고 수요가 폭증함에 따라 여러 회사에서 상업용으로 컴파일러를 만들었고, 이에 따라 코드가 컴파일러에 따라 중구난방이 되는 것을 막기 위해 표준화의 필요성이 제기된다. 그래서 1990년에 ANSI에서 C++ 표준화를 위한 위원회가 설립되었으나 1991년에 ISO로 넘기면서 스트로우스트루프를 비롯한 프로그래밍 언어학자 및 컴파일러 제조사들을 모아 표준화 작업을 하기 시작한다.[37] 그렇게 C++의 표준안이 ISO/IEC 14882:1998라는 공식 명칭으로 처음 제정되었으며, 이를 줄여 C++98이라 부른다.

3.1.5. C++03 (2003년)

2003년에 C++98 문서의 애매모호했던 부분들을 보완한 C++03이 ISO/IEC 14882:2003라는 정식 명칭으로 제정되었는데, 이는 그냥 표준화 문서상 불명확했던 것을 교정한 버전이다. 예를 들어 std::vector가 배열처럼 연속된 메모리를 차지하도록 표준에 명시했다. 사실 당시 컴파일러들 대다수가 이미 C++03 제정 이전부터 이러한 부분들을 잘 구현한지라, 실제 프로그래머 입장에서 변한 건 없다.

3.2. 모던 C++

각종 주요 컴파일러들과 모던 C++의 호환성은 여기서 볼 수 있다.

3.2.1. C++TR1 (2007년)

2005년 6월 24일에 표준 라이브러리를 확장하는 것을 골자로 하는 C++ 테크니컬 리포트의 초안이 작성되어 2007년 11월에 제정되었다.

첫 번째 테크니컬 리포트라고 해서 C++TR1으로 알려져 있는데 혹자는 이를 표준안과 동급으로 간주하고 C++07/TR1이라 부르기도 한다. 정식 명칭은 ISO/IEC TR 19768:2007이며, C++ 표준화 이후 사실상 언어에 변화를 가한 첫 번째 사양이지만 엄밀하게 표준안은 아니라서 컴파일러 제작사가 원하면 넣고 아니면 마는 수준의 선택적인 확장안이었다. 그래서인지 전부 std 네임스페이스에 들어가 있지 않고 대신 std::tr1 네임스페이스로 분리되어 들어간다. 이후 C++TR1의 기능들은 대부분 C++11 표준 사양으로 흡수된다.

C++TR1 초안이 발표되기 2달 전인 2005년 4월 29일에 C++TR2의 초안에 대한 작업을 시작했었다. 여기에는 유니코드, XML/HTML, 네트워킹, 파일 시스템 등의 기능이 포함될 예정이었으나, 이는 무산되었다. 대신, 각각의 라이브러리와 기능들을 "Technical Specifications"이라는 별도의 비표준 기술 사양으로 별도로 제정하기 시작했다. 여기에는 파일 시스템, 병렬 컴퓨팅, 트랜잭셔널 메모리, 컨셉트, Range, 코루틴, 네트워킹, 모듈, 리플렉션 그리고 심지어 그래픽까지 있지만, 이러한 것들이 추후 표준에 포함될 지 또는 얼마나 수정될 지는 미정이다. C++TR2의 일부 사양이 C++11에 포함되었으며, C++17, C++20에 해당하는 기능들까지 포함되어 있는 등 실험적인 성격의 사양임을 짐작할 수 있다.

이러한 시범 사양은 std::experimental이라는 네임스페이스에 포함되어, MSVC Clang에서 일부 지원했다. 시범적인 사양은 이 사이트에서 확인할 수 있다.

3.2.2. C++11 (2011년)

새로운 기능들이 많이 포함되었던 C++TR1이 정식 표준으로 채택되지 못 해서 대부분의 프로그래머들에게 있어서 C++은 C++98 이후 13년 간 정체된 언어나 다름 없었다.

2011년 8월 12일에 최신 컴퓨팅 환경을 지원하기 위해 급격한 변화의 첫 단추를 끊은 새로운 표준안인 C++11이 ISO/IEC 14882:2011라는 정식 명칭으로 최종 승인되었다. 최종 승인되기 전까지 표준안 개발 당시엔 C++0x라 불리었는데, 2000년대 안에는 표준화가 될 것이라는 의미였다. 하지만 결국 2000년대가 끝나고 2011년 8월에서야 정식 C++ 표준이 되어서 11이 붙었다. # 여담이지만 추가된 많은 기능 중 상당수 항목들이 boost 라이브러리를 다수 참조[38]했다. C++11에서 r-value/move[39], constexpr, std::initializer_list, Range-based for loop[40], lambda expression, 스마트 포인터[41], 정규표현식[42], 멀티쓰레드 등등 중요한 신 개념과 편의 기능들이 많이 추가되었으므로 반드시 확인할 필요가 있다. C++11에 추가된 내용만 적절히 활용해도 C++ 코드의 작성과 관리가 보다 명확하고 더 쉬워진다.

C++ 표준 위원회는 C++98에서 C++11까지 업데이트가 나오는 데 시간이 매우 오래 걸린 것이 업데이트가 잘 완성된 후에 발표하려고 했던 접근 방식 때문이었다고 자평한 뒤, 이러한 완벽주의를 버리고 3년에 한 번씩 그때까지 정리된 업데이트만 발표하는 형식(기차 모델)으로 규칙적인 업데이트를 하는 것이 훨씬 생산적인 접근방식이라고 결정하였다.

3.2.3. C++14 (2014년)

2014년 8월에 ISO/IEC 14882:2014라는 정식 명칭으로 C++14이 제정되었다. 표준안 작성 당시 C++0x라 불리었던 C++11처럼 C++14도 C++1y라는 코드네임으로 불렸으며, 주로 C++11에 추가된 요소들을 다듬고 확장하는 데 치중한 비교적 마이너 표준안이다.

3.2.4. C++17 (2017년)

2017년 12월에 C++1z로 알려졌던 C++17 표준이 ISO/IEC 14882:2017이라는 정식 명칭으로 최종 승인되었다.

C++11의 추가 요소들을 다듬고 향상시키는데 주력했던 C++14에 비해, C++17은 표준 라이브러리의 새로운 기능에 비교적 주력한다. 초안 단계에서부터 논란이 많은 표준안이기도 하다. C++11/14의 실정에 맞추어 옛날 기능을 삭제하자는 안이 많기 때문인데[43], deprecated 판정을 받은 std::auto_ptr이나 삼중자(trigraph)[44] 등이 삭제되었다. 이는 옛 코드에 대한 호환성을 포기하는 것이기 때문에, 오래된 코드들이 최신 컴파일러에서 컴파일되지 않을 가능성이 크다. 이 때문에 IBM에서 문자체계의 문제를 내세워 삼중자를 제거하지 말라고 회사 차원에서 표준화 위원회에 진정서를 제출했었다. #

C++17에는 파일 시스템, STL 병렬 알고리즘 처리[45] 등이 추가되었다. 그외의 새로운 기능들은 이곳에 잘 정리되어 있다. 예를 들면 C++17에 추가된 structured binding을 잘 활용하면 두 개 이상의 값을 반환받을때, 값들을 선언과 동시에 초기화하는 기능을 이용할 수 있다.

3.2.5. C++20 (2020년)

2020년에 최종 승인된 C++20은 C++11, C++14, C++17에 이은 주요 버전이다. 비동기 프로그래밍을 지원하기 위한 코루틴[46], 컴파일 속도 향상을 위한 소스 파일 모듈화[47], 템플릿 매개변수에 조건을 추가하기 위한 컨셉트[48] 다양한 기능들이 추가될 예정이다. 그 외에 추후 표준에[49] 추가될 수 있는 리플렉션과 네트워크 라이브러리네트워크 라이브러리인 asio는 또 밀렸다., 2D GUI 라이브러리, 트랜잭셔널 메모리에 대한 시범 사양도 별도로 공개되어 있다.

더 놀라운 점은 C++20에 추가된 기능들과 C++20에 다 반영되지 못한 대신 다음 C++ 표준에 포함될 예정인 기능들이 대부분 C++TR2 제안서에 이미 언급되어 있던 사양이라는 특징이 있다. 표준화로서 최소 15년만에 빛을 발하게 된 셈.

3.2.6. C++23 (2023년)

스택 트레이스(리플렉션), executor 등이 추가될 예정이었으나, 전세계적인 코로나 판데믹으로 인해 표준 위원회의 작업이 더뎌지고 있다. 2022.1월 현재까지 표준에 머지된 큰 변화는 리플렉션 이외에는 거의 없다고 볼 수 있으며, 이외에는 C++20까지의 결점이나 부족했던 기능들 등을 보완하는 패치이다.

4. 방언 및 확장

I don't like dialects because the larger the community is, the more they can share and the better things are.
나는 방언을 좋아하지 않는다. 왜냐하면 커뮤니티가 더 커질수록, 더 많은 사람들이 공유할 수 있게 되고, 상황이 더 좋아지기 때문이다.
- Bjarne Stroustrup, Core C++ 2021

4.1. GPGPU를 위한 확장

4.1.1. CUDA

2007년 6월에 발표된 NVIDIA GPU에서 동작하는 C++ 기반 GPGPU 언어.

4.1.2. OpenCL

2009년 8월에 발표된 개방형 GPGPU 언어. 원래는 C 기반이었으나 2015년 11월 16일에 발표된 OpenCL 2.1부터 C++14를 기반으로 문법이 확장되었다.

4.2. 마이크로소프트의 방언

4.2.1. C++/CLI

2005년 비주얼 스튜디오 2005에 도입된 .NET Framework의 CLR에 맞물려 동작하는 C++. 보통 프레임워크 정도로 제공되는 다른 라이브러리와 달리 .NET의 메모리 구조에 맞추어 언어 자체가 다르다.

2002년에 비주얼 스튜디오 .NET 2002에 도입되었던 Managed Extensions for C++[50]는 정말 못 써먹을 언어였기 때문에 Managed C++을 대체하기 위한 언어로 나왔으며, 적어도 스펙상으로는 게임을 만들기에도[51][52] 부족함이 없는 고속의 언어임에도 생산성은 C++에 비해서 높다고 한다. 특히 C#과 C++를 이어주는 래퍼 등의 모듈을 만들기에는 아주 제격이며, C/C++로 만들어진 프로젝트를 시간적 여유를 두고 닷넷 기반으로 바꾸는 용도로도 많이 쓰이고 있다. 필요에 따라 단계적으로 모듈을 바꿔치기 하다보면 어느새 순수 닷넷 애플리케이션 완성. 그러나 지원 및 이식성이 떨어져서 업계 표준으로 쓰기에는 무리다.

C++/CLI 코드 내에서는 관리와 비관리 클래스를 혼용할 수 있으므로 래핑 없이 모든 모듈을 C++/CLI로 작성해도 되지만, C++/CLI 내의 C++ 클래스는 순수 네이티브 라이브러리에서 만들어진 C++ 클래스에 비해 성능이 다소 떨어진다. 따라서 성능이 중요한 모듈은 별도의 네이티브 코드로 작성하고, C++/CLI는 이 모듈의 래핑을 담당하는 모듈만을 작성하는 것이 좋다.

4.2.2. C++/CX

2012년에 WinRT 및 XAML을 지원하기 위해 마이크로소프트에서 고안한 확장 언어. 라이브러리는 Windows Runtime C++ Template Library(WRL)을 사용했으나 2015년 6월 23일에 발표된 C++/WinRT로 대체되었다.

5. 문법

파일:상세 내용 아이콘.svg   자세한 내용은 C++/문법 문서
번 문단을
부분을
참고하십시오.

6. 도서

보통 기능이 적고 사용 분야가 한정된 언어들과 달리, C++는 기능이 매우 방대하기 때문에 온갖 분야에 다 사용되지만[53] 반대급부로 전문가들도 모든 기능을 능숙하게 쓰는 경우가 거의 없고, 자신이 주로 사용하는 분야에서 쓰는 기능만 파는 경우가 많다. 분야마다 주로, 자주 사용하는 기능이 천차만별이다보니 중상급 이상의 레벨에서는 목적지가 크게 갈린다. 때문에 어디에서 시작할 것인가가 꽤나 학습 방향에 중요하게 작용해서 입문 및 초급자용 도서 추천이 매우 곤란한 편에 속한다.

추가적으로 몇 년도 C++ 버전을 기준으로 썼는지도 고려해야 한다. 역사가 길며 지속적으로 갱신되었다는 특성 때문에 일부 분야에서는 격변 수준으로 바뀐 영역도 많으며, 최신 기능들이 추가되기 전에 저술되었던 예전의 명저들이 현 시대상에는 맞지 않는 경우도 많다. 물론 유명한 서적들의 경우 최신 기능도 포함한 개정판이 나오는 경우가 있지만, 예전 내용 그대로에 최신 기능을 것핥기 수준으로 덧붙인 수준에 불과한 경우도 많아 잘 찾아보아야 한다.

또한 공학 서적이 그렇듯 컴퓨터 공학자들 대부분이 외국인이기 때문에 필독서라고 할만한 책은 대부분 외국 원서를 번역한 번역서인데, 번역에 있어 전문지식이 반드시 필요한 공학 서적이라는 특징이 번역자들 수준이 크게 떨어지는 국내 출판업계의 특성과 겹쳐 상당 수의 번역서들이 절망적인 수준의 번역을 자랑한다. 웬만한 외국 번역서적들 태반에 번역 문제를 지적하는 후기들이 있을 정도다. 심각한 경우는 치명적인 오역으로 내용을 정 반대로 이해하게 만들거나, 이러한 심각한 수준은 아니더라도 오탈자 검수가 제대로 되지 않는 경우가 태반이고, 일부는 컴퓨터 공학에 대한 지식이 전무한 비전공자가 번역하거나 해서 생소한 번역어를 사용하거나 하는 경우가 있다. 이런 문제가 없더라도 원서에서 쉽게 서술된 내용이 직역을 거쳐 이해하기 어려운 딱딱한 문장으로 둔갑하는 경우도 많다.

때문에 능력이 되면 원서를 읽는 것이 낫다. 하지만 공학 분야의 특성상 매우 훌륭하게 번역되거나 아예 한국인 저자가 쉽게 풀어쓴 내용으로도 이해하기 어려운 지식을 영어 원문을 보고 이해하려면 원어민급 독해력이 있어도 어려운 일이기 때문에 차선책으로 그나마 잘 번역된 책을 찾거나, 아니면 아예 한국인 저자가 한국어 기반으로 쓴 책을 먼저 보고, 영어 실력을 좀 쌓은 이후에 아예 원서로 넘어가는 식으로 공부하는 것을 추천한다.

아래의 도서들은 한국어 번역본이 존재하는 도서를 우선시하였다.
입문 & 초급 입문용 서적으로는 외국의 입문서라고 하면 Teach Yourself 시리즈나, Primer 시리즈 등이 가장 처음 볼 책으로 추천되곤 한다. 다만 C++의 분야가 원체 방대한데다 보통 초심자용 책은 상세히 풀어쓰는 경향 때문에 분량이 늘어나는 특성이 있기 때문에 둘 다 엄청나게 두꺼운 편이며, 예제나 서술 방식에 호불호가 크게 갈리는 책이다.
C++ 창시자인 비아네 스트로스트룹 본인이 학부생 수업 교재로 사용할 목적으로 쓴 Programming: Principles and Practice using C++는 보통 컴퓨터 관련 학부 2학년에 존재하는 프로그래밍 방법론 등의 이름을 가진 특정 언어와는 무관한 수업의 교재이지만, C++ 입문서로도 꽤 유용하다.
오래된( 2005년) 책이지만 Effective C++(3판)을 보는 것도 좋다. 번역이 매끄러워 읽기 좋고 이해하기 쉽다.
인터넷으로 배우고 싶다면 모두의 코드를 추천한다.
C 초급용 추천서적 5종, C++ 초급용 추천서적 5종
중급 일단 초급용 서적을 한 권 독파하고 나면, Effective Modern C++, C++ Template: The Complete Guide, C++ Concurrency in Action 정도의 책을 권하는 사람이 많다. 특히 스콧 마이어스의 Effective Modern C++는 거의 필독서에 가깝다.[54]
C언어의 창시자가 쓴 The C Programming Language가 초급자들부터 봐야 하는 책으로 유명한 것에 비하여, C++의 창시자 비아네 스트로스트룹의 The C++ Programming Language는 절대 초급용 서적이 아닌, 중급 이후의 레퍼런스 서적이다.
이후 이 단계까지 성공적으로 도달하였으면, 최종 목표에 따라서 책을 보아야 하는 단계이므로 더 이상 일반적인 추천이 가능하지 않다.

사족으로, "Visual C++ (VC++)"이라고 굳이 제목에 Visual을 넣는 책들은 조금 오래된 서적들이니 주의할 것. "C/C++"이라고 C언어와 C++을 혼용해서 서술하거나, 명색이 C++ 참고서라면서 정작 내용은 C언어에 대한 것만으로 절반 가까이 채운 책들도 있는데, 이런 종류 역시 별로 추천하지 않는다. 기본적으로 MFC 위주의 오래된 책은 피해야 한다.

7. 관련 문서

8. 외부 링크



[1] 예제 코드나 대회 제출용 코드를 보면, 헤더 파일 호출 부분 아래에 using namespace std;를 입력하여 std 네임스페이스를 생략하는 경우가 있는데, 실무에서는 사용하지 말 것을 강력히 권장한다. 다른 네임스페이스에 같은 이름의 함수나 변수가 존재할 수 있으며, 이러한 경우 using문으로 전역 네임스페이스에 포함하면 충돌이 일어날 수 있기 때문이다. 출처 [2] 많은 헬로월드 예제 코드들이 std::endl을 사용하지만, std::endl은 그저 불필요한 오버헤드가 추가된 개행문자일 뿐이고, 헬로월드라는 한줄짜리 출력 프로그램의 스트림에 이것이 추가되면 스트림 연산이 늘어나기만 한다. 위키러들은 std::endl을 빼고 헬로월드를 만들어보자. 출처 다만, 개행문자가 없는 경우 운영체제에 따라서 내용이 실제로 출력되지 않을 수 도 있음에 주의. [3] 헤더파일 구조 대신 C++20에 추가된 모듈을 활용한 예시이다. [4] 간단히 설명하자면, C언어의 확장판이라고 보면 된다. 만약에 위의 헬로 월드 예제가 귀찮거나 C언어풍으로 쓰고 싶다면 그렇게 해도 좋다. [5] 덴마크인이라 이름 역시 덴마크어이며, 북유럽권 언어가 그렇듯이 영미권에서도 상당히 난해하게 들리는 이름이다. 스트로스트룹이라는 이름은 그냥 알파벳 철자대로 영미식 발음으로 읽은 것이라 원래 발음과는 상당히 다르다. 이름 발음을 물어 보는 경우를 하도 많이 들었는지 아예 본인이 자기 사이트에 wav 파일로 이름 읽는 법을 올려두었다. 한국 웹 역시 그냥 영미권 발음대로 적거나 아예 음역조차 하지 않고 알파벳 그대로 적어두기도 한다. [6] 간단하게 'C=C+1' 정도 의미다. 수학교육과 등에서는 '다음수'라는 개념으로 가르친다. 간단히 for문에 들어가는 for (i = 0; i < 10; i++) { foo.bar(); } 을 생각하면 쉽다. [7] 현재의 모던 C++ 표준 라이브러리[56]가 스테파노프(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'라는 초기 명칭과는 한참 차이가 날 정도로 확장되었다. [8] 그러나 실상 C++11 이후의 모던 C++ 기능을 가르쳐주는 곳이 매우 드물어서 C++의 객체 지향만 조금 배울 수 있다. C++의 객체 지향은 다른 언어에 비해서 좀 더 복잡하다. 제대로 다루기 위해서는 별도의 서적을 참고하는 것이 권장되며, 실제 프로젝트의 코드에 대해 탐구하고 경험해보는 것이 좋다. [9] "잘 짜인 C 프로그램은 C++ 컴파일러로 컴파일할 수 있어야 한다"는 말도 1999년에 C99 표준이 나오면서 틀린 말이 되었다. [10] C로 프로그래밍에 발을 들어 놓은 후 C++/Java로 업그레이드 테크를 타는 학생에게 C++ 프로그래밍을 해 오라 하면, C에서는 구조체라는 개념이 있더라도 잘해봐야 C++의 입출력 객체를 이용하는 정도를 넘지 못하고 절차적 프로그래밍을 그대로 따라가는 영락없는 C 스타일의 프로그램이 튀어나오기 일쑤다. [11] f(int a, int b) { ... } f(a, b) int a; int b; { ... } 로 작성하는 것. [12] C++20 [13] iostream [14] stdio.h [15] 시간 제한이 빡빡한 PS(문제풀이) 쪽에서는 거의 매번 쓰일 정도인 기능. [16] 원본 객체가 필요없는 경우, 새로 객체를 생성할 때 원본 객체의 포인터와 값들을 그대로 사용하여, 오버헤드 없이 기존 객체의 메모리를 재활용할 수 있도록 하는 기능이다. [17] constexpr은 C++11에 추가되었지만, if constexpr은 C++17에 추가되었다. [18] Resource Acquisition Is Initialization. 이를 직역하면 "자원 흭득은/이 초기화다."는 뜻으로, 실행 흐름이 특정 범위를 벗어나면 그 안에서 할당한 자원을 다시 반납하도록 만드는 디자인 패턴이다. 해당 범위의 시작과 끝에 newdelete를 사용하여 동적 배열과 같은 힙 영역 메모리의 관리를 직접 수행할 수도 있지만, 이를 하나의 객체에 묶음으로써 해당 범위에서 자동적으로 생성자와 소멸자가 호출되어 메모리를 자동으로 관리되도록 할 수 있다. 이러한 디자인을 이용한 설계는 메모리 누수를 예방할 수 있다는 장점이 있다. 이에 대한 대표적인 예시는 스마트 포인터인 std::unique_ptr 뮤텍스std::lock_guard가 대표적인 예이다. [19] C#을 스트립트 언어로 쓰는 유니티 엔진도 내부 소스는 C++로 이루어져 있다. 즉, 스크립팅용으로만 C#을 쓰는 셈이다. 심지어 C#으로 짜놓은 코드를 C++로 변환하는 IL2CPP라는 기능도 제공한다. [20] Core C++ 2021 :: Bjarne Stroustrup :: Thriving in a crowded and changing world: C++ 2006-2020(1:11:14부터 해당 발언 시작) [21] "... 나는 꼭 '보이지 않는'이란 수식어를 붙이는데, 이것은 매우 빈번하게 C++가 가장 아래쪽의 인프라를 구성하는 요소로 존재하기 때문이다. 우리는 C++를 매우 다양한 방식으로 지금 현재 이용하고 있지만, C++가 눈에 직접 보이진 않는다. 어쩌면 'C++ inside'로고를 오래전에 개발했더라면 좋았을 것같다. intel inside라는 인텔의 마케팅 로고를 패러디하고 있는 듯. 우리는 (C++를) 보지 못하고 있다. 우리는 전화통신을 사용하지만 C++를 보지 못하고 있다. 우리는 카메라를 사용하지만 C++를 보지 못하고 있다. 우리는 OS를 사용하지만 C++를 보지 못하고 있다. 우리는 시그널 프로세싱을 사용하지만 C++를 보지 못하고 있다. 우리는 C++를 안 보려고 하는 경향이 있다. 하지만 C++는 매우 빈번하게 모든 것의 하부에 (설령 그것이 Java, C#, Python등 다른 언어로 작성된 것이라고 하더라도) 존재하고 있다." [22] 다만 해당 벤치마크의 코드는 언어의 성능을 보여주지 못한다. 그 예시로 fannkuch-redux테스트에서 C++는 수동 최적화한 SIMD를 사용하고 있지만 C코드는 순수 스칼라에 컴파일러 Auto-vectorization에만 의존한 상태로 연산하고 있기 때문이다. 그 반면에 C코드가 더 빠른 spectral-norm 코드의 경우 C++는 AVX128, C는 AVX256를 사용하고 있고 그에 따라 두배의 스루풋을 보여주고 있기 때문에 순수하게 1:1 언어적 특성에서 보이는 성능 향상을 보여주고 있지 못하다. Java는 JVM이라는 ISA Neutural 한 환경이므로 수동 어셈블리 최적화 같은 하드웨어 가속을 사용하는것이 불가능한데다 애초에 해당 벤치마크 목록들에서 언급하고 있는것이 "These are only the fastest programs. If it's manually vectorized SIMD, does the host language matter?" - 라고 수동 SIMD최적화를 한다면 호스트 언어가 의미가 있는가 라고도 언급하고 있기도 하다. 또한 C++의 경우 클래스와 템플릿을 사용하고 이를 C에서 비슷하게 구현하는 경우 대체로 Context기반의 프로그램 모델을 사용하는데 단순히 class newmalloc(struct) + member만으로 비교해도 C++런타임에서 다루는 추가적인 오버헤드가 없어 C의 성능이 더 잘 나오게 된다. [23] 우스갯 소리로 클래식 c++ 문법서 한 권, 모던 c++ 교재 한 권, STL 한 권, 해서 총 3권을 독파하면 이제 c++ 입문자가 된다는 이야기가 있다. [24] Bjarne Stroustrup이 직접 쓴 TC++PL이 1,300쪽이다. 물론 이 책은 레퍼런스용 책이기 때문에 TC++PL의 서문에는 아예 'C++를 시작하는 입문자에겐 이 책을 권장하지 않는다'는 말이 적혀 있다. [25] IT 업계에서 최고 수준의 시니어 프로그래머들을 많이 보유하고 있는 구글 조차도 블링크의 메모리 관리를 못해서 지금도 누수와 함께 버그들이 튀어 나오는 판이고. 모질라도 차세대 엔진을 후술할 Rust로 변경할 정도이다. [26] Core C++ 2021 :: Bjarne Stroustrup :: Thriving in a crowded and changing world: C++ 2006-2020(1:12:36부터 해당 발언 시작) [27] 1990년대 중반 [28] 조건이 맞지 않는 템플릿이 들어오면 고의적으로 컴파일 에러를 발생시키는 트릭 [29] 대부분 프로그래머에겐 의미가 없지만, 컴파일러 제작자에게는 의미가 있다. concept의 구현은 SFINAE 기반이기 때문이다 [30] dynamic polymorphism을 원한다면, 쓰기 싫어도 무조건 쓸 수 밖에 없다. [31] 예외를 던지지 않는 실행경로에서는 아무 것도 하지 않아도 되도록 만드는 기법. 요약하면 컴파일러가 전역 에러 핸들러 테이블을 미리 만들어두어, 예외를 던지면 런타임에 해당 테이블을 참조하여 처리하는 방법이다. 이를 구현한 방법은 주로 SEH(MSVC 런타임)와 DWARF(GCC 런타임)가 사용된다. 저 기법이 등장하기 이전까지는 SJLJ를 사용하여 예외처리를 구현하였다. [32] 처리하기 전에 앞서 확인하는 과정을 통해 피할 수 있는 예외는 당연히 피하는 것이 좋다. 예를 들면, std::optional에 유효한 객체를 가지고 있지 않을 때 객체에 접근하여 발생하는 std::bad_optional_access 예외는 접근하기 전에 미리 확인하여 피할 수 있다. 그러나 컨테이너 라이브러리 등을 사용하는 과정에서 std::bad_alloc과 같이 메모리 할당의 실패로 발생하는 예외는 남은 메모리 크기를 확인하더라도 피하지 못할 수 있다. 이 경우, 예외를 처리하지 않으면 프로그램은 종료될 뿐이다. [33] two-phase construction 등 회피 꼼수가 있으나 어디까지나 꼼수다. C++ 특성상 예외를 완전히 피하면서 깔끔한 코드를 설계하는 것은 매우 어렵다. [34] 메이저 컴파일러들 중 일부는 RTTI와 마찬가지로 예외 처리를 끄는 기능을 제공하긴 한다. 하지만 이 경우 표준 라이브러리를 사용하는 중 거기서 예외를 던지는 순간 정의되지 않은 동작(undefined behavior)으로 돌입한다. 이 순간에는, 컴파일러나 런타임 라이브러리에 따라 다르긴 하지만, 대부분의 경우 프로그램이 종료된다. [35] P0709에 따르면 무려 52%의 프로그래머가 프로젝트 내에서 예외가 금지되었다고 답하였다. [36] 이러한 개념을 먼저 도입한 언어의 예로 Rust KotlinResult를 들 수 있다. Rust는 예외 대신 Result<T, E> 객체와 열거형 에러를 사용하며, From 트레이트가 구현되어 있는 경우 ? 연산자에서 자동 변환을 지원한다. [37] C++뿐 아니라 C, 파스칼 등 클래식 언어들이 전부 ISO에서 표준화되고 있다. 아예 이런 언어를 표준화하는 전문 태스크 팀 JTC1/SC22라는 게 있으며, 이중 C++는 21번째 그룹에 속한다. [38] 아예 boost 라이브러리 개발진 중 다수가 C++ 표준화 위원이기도 하다. 차세대 C++에 들어갈 요소를 표준화 위원들이 미리 개발해서 시험해본 것이라 봐도 무방하다. [39] 객체의 복사에서 원본이 불필요한 경우 이를 활용하면 실행 시간 감소의 효과가 있다. 복사 생성자를 통해 객체를 복사하고 원본의 메모리를 삭제하는 대신, 얕은 복사처럼 기존의 포인터만 가져오고 원본 역시 포인터만을 소멸시켜 객체 자체는 그대로 사용한다. 이동 생성자와 이동 대입 연산자를 통해 이러한 기능을 구현한다. [40] 표준 라이브러리의 iterator 대신 이를 대신 사용함으로써 for문을 간략하게 작성할 수 있다. [41] 기존 표준에서 유일한 스마트 포인터였던 std::auto_ptr은 명시적이지 않은 소유권 이동, 동적 배열의 삭제 불가 등 실무에서 절대 사용하면 안되는 쓰레기였지만, std::shared_ptr와 같은 새로운 스마트 포인터가 추가되면서 메모리 관리가 보다 안전해지고 편해졌다. 스마트 포인터를 잘 활용한다면 메모리 관리에 대한 수고를 덜 수 있다. 실제로 Chromium과 같은 대규모 프로젝트에서 심심치 않게 볼 수 있다. [42] 다만, 속도가 Python보다도 1.5배나 느리다. 컴파일러들의 구현이 좋지 않거나 운영체제의 메모리 할당이 너무 잦기 때문으로 보인다. 자주 사용한다면 별도의 라이브러리를 사용할 것을 추천한다. [43] C++11 제정 당시에도 std::auto_ptr 등 많은 옛날 요소들이 deprecated로 명시되어 사용이 자제될 것을 권장받았으나, 이는 어디까지나 권고라 사용자가 맘 놓고 쓰려면 쓸 수 있었다. 이전 버전의 기능을 아예 죽여버린 사례는, C++98 시절에 실상 누구도 안 썼고 지금은 아예 용도가 바뀌어버린 auto 등 극소수만이 해당한다. [44] C/C++의 특수기호는 미국의 부호체계인 ASCII를 기반으로 하고 있기 때문에 해당 영역에 다른 문자를 넣어 사용하던 유럽국가들의 코드페이지에서는 이상하게 표현되는 일이 있었다. 그래서 잘 사용하지 않는 문자의 조합으로 특수문자를 표기하는 방법. 문자 2개의 조합이면 digraph, 3개면 trigraph. 유니코드가 대세가 된 지금은 사실 필요 없고, 옛날 소스라고 해도 간단한 일괄 변환작업으로 새 표준에 맞출 수 있기도 하다. 다만 표준에서 삭제처리를 할 경우 해당 문법이 다른 기능으로 대체될 수 있기 때문에 일괄변환이 언제나 될 것이라는 보증이 안 되고, 후술하는 IBM 등 장기간의 낡은 코드와 관련된 이해관계자들 입장에서 떨떠름한 것은 사실이다. [45] 다만 이를 제대로 지원하는 컴파일러는 그다지 많지 않아서 호환성 문제가 있을 수 있다. Visual Studio의 경우 2017 이상, GCC의 libstdc++의 경우 9 이상에서 지원한다. [46] 언어 차원에서 지원하기 때문에, 비동기 프로그래밍을 위해 별도의 라이브러리가 필요하지 않게 된다. [47] 소스 파일이 각각의 헤더 파일을 일일이 포함하는 대신, 미리 컴파일된 모듈을 사용함으로써 컴파일 속도가 크게 향상된다. 별도의 시범 사양으로도 공개되어 있기 때문에, Visual Studio의 C++ 컴파일러와 Clang 컴파일러에서 이미 시범적으로 지원하고 있다. [48] 현존하는 템플릿 트릭의 대다수를 대체할 수 있으므로 코드가 간결해지고 가독성이 증가한다. [49] 3년 주기를 따를 경우 C++23이 된다. [50] 줄여서 Managed C++라고도 불렀다. [51] 게임은 일반적으로 생산성이 매우 중요한 거대 프로젝트이면서 하드웨어의 성능을 극한까지 끌어내야 하는, 속도와 효율에 매우 민감한 특수한 소프트웨어 분야이기 때문에 C++가 많이 사용된다. 괜히 이런 이야기에 게임에 대한 언급이 잘 나오는 게 아니다. [52] 초기의 Managed C++은 언어의 포지션도 상당히 애매했다. 간단히 말하자면 닷넷 프레임워크와 레거시 운영체제 프레임워크를 동시에 지원하고자 했으나 결과는 이도저도 아니었다. 차라리 닷넷 프레임워크가 COM을 지원하니 그런 걸 쓰는 게 낫다. [53] 시스템, 실시간 임베디드, 게임, 파이낸스, 그래픽, 머신러닝, 영상처리, 수치 시뮬레이션... [54] 하지만 한국어판은 번역 오류가 많아 이해가 어려울 수도 있다. [55] 대부분의 컴공과에서 배운다. 많은 컴공과에서 첫 언어로 C를 가르치는 관계로, 학생들에게 익숙한 C의 문법을 가지고 객체지향 프로그래밍의 개념도 다룰 수 있다는 장점이 있기 때문이다. Java나 Python을 처음으로 배우는 학교의 경우에도 언젠가 한번쯤은 C나 C++를 건드릴 일이 생기게 된다.