mir.pe (일반/밝은 화면)
최근 수정 시각 : 2024-10-26 22:09:45

C++

CPP에서 넘어옴

[[컴퓨터공학|컴퓨터 과학 & 공학
Computer Science & Engineering
]]
[ 펼치기 · 접기 ]
||<tablebgcolor=#fff,#1c1d1f><tablecolor=#373a3c,#ddd><colbgcolor=#0066DC><colcolor=white> 기반 학문 || 수학( 해석학 · 이산수학 · 수리논리학 · 선형대수학 · 미적분학 · 미분방정식 · 대수학( 환론 · 범주론) · 정수론) · 이론 컴퓨터 과학 · 암호학 · 전자공학 · 언어학( 형태론 · 통사론 · 의미론 · 화용론 · 음운론) · 인지과학 ||
하드웨어 구성 SoC · CPU · GPU( 그래픽 카드 · GPGPU) · ROM · RAM · SSD · HDD · 참조: 틀:컴퓨터 부품
기술 기계어 · 어셈블리어 · C/ C++ · C# · Java · Python · BIOS · 절차적 프로그래밍 · 객체 지향 프로그래밍 · 해킹 · ROT13 · 일회용 비밀번호 · 사물인터넷 · 와이파이 · GPS · 임베디드 · 인공신경망 · OpenGL · EXIF · 마이크로아키텍처 · ACPI · UEFI · NERF · gRPC · 리버스 엔지니어링 · HCI · UI · UX · 대역폭 · DBMS · NoSQL · 해시( SHA · 브루트 포스 · 레인보우 테이블 · salt · 암호화폐) · RSA 암호화 · 하드웨어 가속
연구

기타
논리 회로( 보수기 · 가산기 · 논리 연산 · 불 대수 · 플립플롭) · 정보이론 · 임베디드 시스템 · 운영 체제 · 데이터베이스 · 프로그래밍 언어{ 컴파일러( 어셈블러 · JIT) · 인터프리터 · 유형 이론 · 파싱 · 링커 · 난해한 프로그래밍 언어} · 메타데이터 · 기계학습 · 빅데이터 · 폰노이만 구조 · 양자컴퓨터 · 행위자 모델 · 인코딩( 유니코드 · MBCS) · 네트워크 · 컴퓨터 보안 · OCR · 슈퍼컴퓨터 · 튜링 머신 · FPGA · 딥러닝 · 컴퓨터 구조론 · 컴퓨터 비전 · 컴퓨터 그래픽스 · 인공지능 · 시간 복잡도( 최적화) · 소프트웨어 개발 방법론 · 디자인 패턴 · 정보처리이론 · 재귀 이론 · 자연어 처리( 기계 번역 · 음성인식) · 버전 ( 버전 관리 시스템 · Git · GitHub)

프로그래밍 사이트 선정 프로그래밍 언어 순위 목록
{{{#!wiki style="margin: 0 -10px -5px; word-break: keep-all"
{{{#!wiki style="display: inline-table; min-width: 25%; min-height: 2em;"
{{{#!folding [ IEEE Spectrum 2024 ]
{{{#!wiki style="margin: -5px 0"
<rowcolor=#fff> 스펙트럼 부문 상위 10개 프로그래밍 언어 직업 부문 상위 10개 프로그래밍 언어
1 Python 1 SQL
2 Java 2 Python
3 JavaScript 3 Java
4 C++ 4 TypeScript
5 TypeScript 5 SAS
6 SQL 6 JavaScript
7 C# 7 C#
8 Go 8 HTML
9 C 9 Shell
10 HTML 10 C++
}}}
}}}
}}}
[ Stack Overflow 2024 ]
[ TIOBE 2024 ]
||<tablewidth=100%><width=9999><-4><bgcolor=deepskyblue><tablebgcolor=#fff,#222> 2024년 8월 기준 검색어 점유율 상위 20개 프로그래밍 언어 ||
1 Python 11 MATLAB
2 C++ 12 Delphi / Object Pascal
3 C 13 PHP
4 Java 14 Rust
5 C# 15 Ruby
6 JavaScript 16 Swift
7 SQL 17 Assembly language
8 Visual Basic 18 Kotlin
9 Go 19 R
10 Fortran 20 Scratch
{{{#!wiki style="margin: 0 -10px -5px; min-height: calc(1.5em + 5px);"
{{{#!folding [ 21위 ~ 50위 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px"
21 COBOL 36 Scala
22 Classic Visual Basic 37 Transact-SQL
23 LISP 38 PL/SQL
24 Prolog 39 ABAP
25 Perl 40 Solidity
26 (Visual) FoxPro 41 GAMS
27 SAS 42 PowerShell
28 Haskell 43 TypeScript
29 Dart 44 Logo
30 Ada 45 Wolfram
31 D 46 Awk
32 Julia 47 RPG
33 Objective-C 48 ML
34 VBScript 49 Bash
35 Lua 50 Elixir
}}}}}}}}} ||
[ PYPL 2024 ]

}}} ||
프로그래밍 언어 목록 · 분류 · 문법

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


<colcolor=#00599c,#659AD2> C++
파일:C++ 로고.svg
개발 비야네 스트로스트룹
버전 23
파일:홈페이지 아이콘.svg

1. 개요
1.1. C와의 차이점1.2. 객체 지향 프로그래밍1.3. 일반화 프로그래밍1.4. 메타 프로그래밍1.5. 용도
2. 문법3. 언어 명세4. 표준 라이브러리5. 역사
5.1. 모던 C++
5.1.1. C++265.1.2. C++235.1.3. C++205.1.4. C++175.1.5. C++145.1.6. C++11
5.2. 클래식 C++
6. 평가
6.1. 장점
6.1.1. 무비용 추상화6.1.2. 많은 시스템에서 지원하는 언어6.1.3. 수많은 기능
6.2. 단점
6.2.1. 높은 학습 난이도6.2.2. 메모리 관리 & 보안6.2.3. 애매한 위치
6.2.3.1. C언어와의 호환성6.2.3.2. 템플릿의 난해성
6.2.4. 성능 저하 요소
6.2.4.1. RTTI6.2.4.2. 예외
6.2.5. 패키지 관리자
7. 학습 자료8. 방언 및 확장
8.1. GPGPU를 위한 확장8.2. 마이크로소프트의 방언
8.2.1. C++/CLI8.2.2. C++/CX8.2.3. C++/CL
9. 관련 문서10. 외부 링크

[clearfix]
#!syntax cpp
import std; // 또는 import <print>;

int main() {
    std::println("Hello, world!");
}

1. 개요

“A light-weight abstraction programming language”
“가볍게 추상화한 프로그래밍 언어”
- Bjarne Stroustrup
덴마크의 컴퓨터 과학자 비야네 스트로스트룹(Bjarne Stroustrup)[1] C언어를 바탕으로 제작했다. 1979년에 C언어에서 직접적으로 파생된 C with Classes라는 이름의 언어로 시작되었다가, 1983년에 지금의 이름을 갖게 되었다. 그리고 객체 지향 및 일반화 프로그래밍과 같은 멀티 패러다임을 지원하는 프로그래밍 언어이다.
C언어에서 ++라는 것은 원래 값에 1을 더해서 대입하라는 뜻이다.[2] C는 B 언어를 계승한다는 의미에서 C가 되었는데 왜 D가 아니라 C++가 되었냐 하면, C 언어를 거의 그대로 두고 필요한 만큼만 향상시켰기 때문이라고 한다. 비슷한 이름인 C#과의 차이도 이러한 점에서 나타난다.쓸 때는 C++라고 쓰지만 읽을 때는 시 플러스 플러스, 혹은 줄여서 시플플이라고 읽는다. 한국에서는 사실상 속어에 가까운 씨쁠쁠로 불린다. '씨뿔뿔'로 발음되는 경우도 많다. 교수들이나 풀네임으로 ‘씨 플러스 플러스’라고 읽는 정도. 나이가 꽤 있는 교수 중에는 간혹 '시 더블 플러스'라고 읽는 경우도 있다. 미국에서는 그냥 씨 플러스 플러스 또는 확장자 명인 CPP(시피피)라고 읽는다.

한국에서 컴퓨터공학과를 다닐 경우 배울 수 있는 프로그래밍 언어 3개 중에 속한다. 나머지 둘은 C Java.[3] 학교마다 다르지만 1학년때 아예 다른 C, Python을 배우고 2학년 1학기때 Java, 2학기때 C++ 과목을 수강하는 곳도 있다. 예시로 서울대학교의 경우에는 1학년 1학기에 Python으로 코딩을 입문하고, 1학년 2학기에 C언어로 어느 정도 큰 프로젝트를 짜 보고, 2학년 1학기에 C++과 Java를 배우면서 객체 지향 프로그래밍을 배우게 된다.

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

C++의 창시자는 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.[4]

1.1. C와의 차이점

초기 C++가 C언어를 기반으로 시작했기 때문에 지금도 대부분의 C 프로그램은 C++ 컴파일러에서도 문제없이 컴파일된다. 초기 C++ 컴파일러는 일단 C++ 코드를 C로 변환하고 그걸 C로 재컴파일하는 방식을 사용했을 정도. 다만, C가 항상 C++의 부분집합이었던 것은 아니라 지금도 구조체같이 몇몇 부분에서 차이점이 있다. "잘 짜인 C 프로그램은 C++ 컴파일러로 컴파일할 수 있어야 한다"는 말도 1999년에 C99 표준이 나오면서 틀린 말이 되었다. 순수 C 소스 코드를 C++로 컴파일 할 때 문제의 여지가 생길 가능성이 있다.

그러나 C++는 C와 프로그래밍 패러다임이 크게 다르다. C는 태초부터 절차 지향으로 설계된 운영체제를 만들기 위한 언어였으며, 지금도 메모리와 각종 저수준(low level) 프로그래밍을 위한 언어이기 때문이다[5]. 반면 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)
[6]
그외에 _Generic(type)을 사용한 자료형에 대해 안전한 매크로의 지원 등 C에서만 지원되는 문법이 몇몇 있다.

클래스 비교 코드
#!syntax cpp
// C 스타일
typedef struct GameObjectTag { long id; float x, y, z; } GameObject;

static GameObject* CreateObject() {
    static long __gobject_id = 1;
    GameObject* result = (GameObject*) malloc(sizeof GameObject);
    if (result) {
        result->id = __gobject_id++;
    }

    return result;
}
static void InitObject(GameObject* obj, long id) { obj->id = id; obj->x = 0; obj->y = 0; obj->z = 0; }
static void InitObject(GameObject* obj, long id, float x, float y, float z) { obj->id = id; obj->x = x; obj->y = y; obj->z = z; }
static void MoveObject(GameObject* obj, float x, float y, float z) { obj->x = x; obj->y = y; obj->z = z; }

#include <iostream>
 
struct Foo
 { 
    int n ; 
    Foo ( ) 
    { 
        std :: cout  <<  "statikus konstruktor \n " ; 
    } 
    ~Foo ( ) 
    { 
        std :: cout  <<  "statikus destruktor \n " ; 
    } 
} ;
 
Foo f ;  // statikus objektum
 
int main ( ) 
{ 
    std :: cout  <<  "főfüggvény \n " ; 
}

// 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<float&, float&, float&> GetPosition() noexcept { return std::tie(x, y, z); }

    [[nodiscard]]
    constexpr std::tuple<const  float&, const  float&, const  float&> 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에 비약적인 속도 향상을 가져올 수 있다 [9].

1.2. 객체 지향 프로그래밍

C++는 객체 지향 프로그래밍을 지원하지만, C++의 객체 지향은 다른 객체 지향 언어에서와는 성격이 좀 다르다. 대다수의 객체 지향 언어에서는 많은 부분을 런타임에 처리하며 메모리를 자동으로 관리해준다. C++은 기반 프레임워크가 존재하지 않아 클래스가 어떤 일급 객체가 아니라, 일반 변수처럼 메모리 덩어리일 뿐이다. 최대한 많은 것을 컴파일 타임에 처리하는 것을 지향하고 동적인 메모리 할당을 지양한다. 그리고 결정적으로 메모리 등을 프로그래머가 직접 관리하게 하기 때문에 클래스를 포함해 전반적인 프로그램 설계 자체가 상당한 차이를 보이게 된다.

Java 등의 다른 객체 지향 언어에서와 같은 방식으로 C++ 클래스를 디자인하면 거의 틀림없이 런타임 오류 또는 메모리 문제가 발생한다. 특히 쓰레기 수집을 지원하는 Java로 입문하여 C++로 갈아타는 테크를 탄 학생이라면 처음에는 메모리가 줄줄 새는 프로그램을 만들게 될 것이다. 반면 C++ 스타일을 숙지하고 다른 객체 지향 언어에서 프로그래밍을 하는 경우 특별히 안 될 것은 없지만, 해당 언어의 원어민 스타일로 작성한 코드에 비해 시간이 많이 걸리고 클래스 및 프로그램 구조가 지나치게 경직되는 경향이 있다. 소멸자가 호출되지 않는 등의 차이점은 있지만 심각한 문제가 되는 경우는 많지 않다.

이러한 차이가 생기는 것은 대부분의 객체 지향 언어는 직접적으로 Smalltalk의 영향을 받은 반면, C++는 Smalltalk보다 먼저 객체 지향의 초보적인 개념을 제시한 시뮬레이션 전용 언어인 Simula에서 직접 영향을 받았기 때문이다. [10] 이는 C 프로그램과의 호환성을 고려한 결과이기도 하지만, 기본적으로 C++에서 프로그램의 성능을 희생시키지 않기 위해서였다. Smalltalk의 경우 당시의 기술적 한계도 있고 해서 C보다 대체로 수십 배 정도 느렸고, 이는 당시 C++에서 지향하는 결과가 아니었기 때문이다. 이런 차이 때문에 Java, C# 등의 언어를 먼저 접한 프로그래머들은 C++의 객체지향이 짝퉁이라며 싫어하기도 한다. 하지만 순수 객체 지향 프로그래밍 언어임을 전면에 내세우는 Java나 C#과 달리, 객체지향 패러다임 지원하는 다중 패러다임 언어인 C++ 입장에서는 얼마나 순수하게 객체 지향의 이상을 잘 따르는가보다, C++가 제공하는 온갖 패러다임까지 아우르는 내적 일관성이 더 중요하므로 해당 언어들과 C++를 동일선상에 놓고 비교할 수는 없다.

대표적인 예시로 인자로 전달하는 값은 기본적으로 복사되는 것, 함수 내부에서 객체를 동적 할당하고 주소를 반환하는 것이 있다. 이는 C++에선 메모리 문제를 일으키기 쉬운 방식이라서 설계 단계부터 이런 동작을 배제해야할 필요가 있다. 거의 대부분의 프로그램은 실행되고 있는 현재 문맥에서의 지역 변수를 스택 영역에 저장하는데, 한 문맥의 실행이 완료되면 그 함수의 스택 영역 변수를 위한 메모리는 모두 삭제된다. 그리고 이 변수는 쓰레기 데이터로 남아있다가 다른 함수를 호출할 때 덮어씌워진다. 공유 자원과 같은 특정 데이터를 유지하려면 힙 영역에 할당해야 한다. 가비지 컬렉션이 자동으로 되는 언어에서는 언어 설계 차원에서 메모리에 대한 걱정을 덜기 위해 사용되지 않는 메모리를 자동으로 해제 해주기 때문에 매우 흔히 사용되는 방법이다. [11]

그러나 문제가 생기는 건 오래된 스타일의 C++ 코드에서나 그런 것이고, Modern C++에서는 스마트 포인터를 사용하거나 이를 이동 연산[12]을 통해 그들과 완전히 같은 구조를 구현 가능하기 때문에 더 이상 해당이 없는 사항이다. 예를 들어 한 동적 객체를 여러 소유권자가 공유하는 경우 (참조 대상에 대한 횟수를 기록하는) 레퍼런스 카운팅을 지원하는 std::shared_ptr를, 단일 소유자만 존재하는 경우 std::unique_ptr를 사용한다 [13].

1.3. 일반화 프로그래밍

C++에서는 템플릿을 이용한 일반화 프로그래밍(Generic Programming)이 매우 폭넓게 사용된다. 특히 C++11을 시작으로 하는 모던 C++는 일반화 프로그래밍을 빼고 이야기하는 것이 불가능하다. 당장 매우 널리 사용되는 문자열 클래스 std::string만해도 실제로 들여다보면 std::basic_string<char, std::char_traits<char>>와 같은 형태의 클래스 템플릿의 특수화에 지나지 않는다.

일반화 프로그래밍의 결과물으로는 C++ 표준 라이브러리의 일부분으로 포함된 컨테이너, STL 같은 것들이 있으므로, C++ 표준 라이브러리를 사용하는 순간 일반화 프로그래밍의 도움을 받는 것이다. 따라서 '나는 C++를 사용하지만 템플릿을 이용한 일반화 프로그래밍은 어려우니까 패스하겠다'는 말은 애초에 성립하지 않는다. 또 다른 예시로, C와는 달리 배열 생성에 포인터 혹은 []을 사용하는 배열 대신에 std::vector를 기본으로 사용하라고 가르치는데, 이 std::vector 또한 저장될 원소의 자료형을 std::vector<int>와 같이 템플릿 매개변수로 받아들이는 템플릿 클래스다. 중급 이하 개발자는 라이브러리를 그냥 가져다 쓰면 되고, 직접 만들어 쓸 필요는 없다. 이는 중급 개발자 정도로는 템플릿을 사용한 일반화 프로그래밍 기법을 정확하게 적용하는 것이 무척 까다롭고, 디버깅할 때 이해 불가능한 컴파일러 메세지를 받게 되는 경우가 많아 오류를 수정하기도 어렵기 때문이다. 현재는 C++20에서 concept의 등장으로 이전보다 상대적으로 알아보기 쉬운 템플릿 작성이 가능해졌고, 컴파일 오류 메세지또한 좀 더 이해하기 쉽고 간결하게 표시되게 되었다.

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

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

1.4. 메타 프로그래밍

TMP (Template Meta Programming). 템플릿을 사용한 메타 프로그래밍 Wikipedia
C++에서는 템플릿을 사용해 메타 프로그래밍을 할 수 있다. 여기서 메타 Meta Data (서술 자료)를 의미한다. 값 뿐만 아니라 프로그램, 바이너리, 속성 그 자체도 데이터로 취급한다. 가령 Python의 메타 클래스는 '클래스'를 생성하며 [14] 클래스의 바이트 크기, 필드, 메서드 등을 정의한다. 또한 Python, C#, Swift 등 많은 언어가 int, double 같은 원시 자료형도 클래스로 보여준다. 하지만 C++은 그럴 수가 없다. C++의 클래스는 메모리 덩어리고, int라는 단어 자체에는 그저 컴파일러가 int를 메타 정보로 가진 객체는 32비트 크기로 읽겠다, 말고는 아무 의미도 없다. 다른 언어는 '정보[15]의 메타 정보[16]의 메타 정보[17]를 언어 자체적으로 가져오고, 심지어 정의할 수도 있다. C#, JavaScript, Python이 대표적인 예시다. 반면 C++에선 클래스가 그냥 메모리 덩어리고 메서드도 그냥 쓰기 좋게 만든 함수 포인터다. 전처리기, 컴파일러 확장, 미리 쓰여진 코드에 의존할 뿐 자료형 자체에 대해서는 아무것도 알 수 없다. sizeof, alignof, alignas 키워드가 있지만 부족하다. 그리고 직접 클래스 자체의 속성을 정의하려면 using, static 변수를 사용해 일일히 모든 클래스에 메타 정보를 기입해야 한다. 이 방법은 임의의 클래스, 그리고 원시 자료형에는 적용할 수 없는 문제가 있다. 이 간극을 메우기 위해 대신 템플릿을 사용해 메타 프로그래밍을 시도하는 것이다.

같은 템플릿 문법을 사용하지만 앞 문단의 일반화 프로그래밍과는 엄연히 목표가 다르다. 일반화 프로그래밍이 자료형을 드러내어 컴파일 시기에든 실행 시기에든 코드 확장 및 범용성 증대를 목표로 한다면, 메타 프로그래밍은 명시해야할 자료형을 숨기고, 어떤 자료형에 대해 그 자료형에 대한 정보를 기술하는 것을 목표로 한다. 이는 일반화 프로그래밍과는 다르게 확장이 아니고 특정 자료형에만 코드를 생성하도록 만든다.

그렇다면 C++에서 그냥 메타 프로그래밍이라고 안하고 굳이 앞에 템플릿이라고 붙인 이유가 있을까? C++의 템플릿은 독특한 특성을 많이 가지고 있다. 템플릿 매개변수는 반드시 컴파일 시점에 결정된다. 템플릿 매개변수는 자료형 뿐만 아니라 값도 전달할 수 있다. 템플릿의 실제 코드는 늦게 평가된다. 템플릿은 분명 존재하는 C++ 코드이지만, 실질적으로 전처리기 구문과 다름이 없다. 실행 시점에는 이미 바이너리로 모든 경우에 대해 완성된 상태가 되므로 컴파일 이후에 실행 시간에는 아무 영향이 없다 [18]. 컴파일러가 예측할 수 없는[19] 네트워크 접근, 다중 스레드 작업, 그리고 운영제체 호출을 제외하면 모두 상수 시간에 결정할 수 있다. 곧 C++의 메타 프로그래밍은 자료형의 상태를 기술하는 것에 더해서, 할 수 있는 작업은 컴파일러를 고문해 모조리 미리 처리하는 것이 지상과제가 된다. 이는 C++의 템플릿 문법이 컴파일 시간에 Turing complete하기 때문에 이런 일이 가능한 것이다. C++ 안에 컴파일러 전용의 또 다른 언어가 숨어있는 것과 같은 상황이다. 다른 언어에서는 비슷한 것도 찾기 힘들다.

* Expression Templates: 디자인 패턴의 일종인 Proxy pattern 기반의 Lazy evaluation이 적용되는 효율적인 계산 코드를 컴파일 시점에 생성하는 기법이다. 일반적으로 연산 도중의 임시 객체 생성 문제를 이 기법을 통해 해결하는 경우가 있다. RVO(Return Value Optimization)를 감안하더라도 C++ 특성상 연산자를 활용하는 과정에서, 직접적인 연산을 시도하면 임시 객체의 생성을 완전히 막을 수는 없기 때문이다.

배우기 어렵고 알아보기도 힘들고, 실행 성능을 포기하지 않는 대신 컴파일 시간을 심각하게 포기했다. 심지어 디버깅도 힘들다. 다만 디버그 문제는 자기가 원하는 템플릿에 맞춰 중단점을 거는 게 가능해지는 등 많이 개선된 편이다. 당연히 템플릿에 대하여 깊은 이해가 없다면 아예 이해할 수가 없는 개념이기도 하다. 그래도 알아두면 은근 써먹을 데가 많다. C++에서는 단순히 특정 자료형의 속성 선언부터 함자 클래스[20]를 이용한 방문자 패턴, 트레잇, 타입 리스트, CRTP, mixin 등 수많은 고성능 디자인 패턴은 C++에서는 TMP의 도움 없이는 시도조차 할 수 없다. C++17 기준으로 쓰여진 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

아래는 템플릿을 활용해 컴파일 시점에 계산하는 코드이다. [21]
템플릿 코드
#!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 또는 C++20의 consteval을 활용하면 더 간결하게 쓸 수 있다.
#!syntax cpp
template<size_t N>
constexpr size_t Fibonacci() noexcept // C++20 이후에는 consteval로 지정해도 문제없다.
{
	if constexpr (N < 2) return N;
	else return Fibonacci<N - 1>() + Fibonacci<N - 2>();
}

int arr[Fibonacci<7>()];


그리고 실무에서 가장 많이 쓰이는 테크닉 중 하나로 CRTP (Curiously Recursive Template Pattern) 패턴이 있다. 추상 클래스를 구현할 시 가상 함수 호출에 따른 오버헤드를 막고자 고안되었다. 이는 다음과 같이 작성한다.

CRTP 예제 코드
#!syntax cpp
template <typename Derived>
class Base
{
    constexpr void Function() noexcept(noexcept(Cast()->Function()))
    {
        Cast()->Function();
    }

protected:
     constexpr Derived* Cast() noexcept
     {
         return static_cast<Derived*>(this);
     }

     constexpr const Derived* Cast() const noexcept
     {
         return static_cast<const Derived*>(this);
     }
};

class CRTPDerived1 : public Base<CRTPDerived1>
{
public:
    void Function() noexcept // Base::Function은 상수식이 아니고 noexcept(true)
    {
        std::print("CRTPDerived1");
    }
};

template <typename... Ts>
class CRTPDerived2 : public Base<CRTPDerived2<Ts...>>
{
public:
    constexpr void Function() // Base::Function은 상수식이지만 noexcept(false)
    {
        throw "CRTPDerived2";
    }
};
파생 클래스를 템플릿 인자로 기반 클래스에 넘겨줌으로써, 기반 클래스는 멤버 함수의 동작을 각 파생 클래스에 대해 특수화할 수 있다. 템플릿 인자는 컴파일 시점에 알 수 있는 내용이므로, 프로그램 내에서 기반 클래스가 어떤 파생 클래스로 구현되는지 컴파일 시점에 알 수 있다. 따라서 런타임에 가상 함수 디스패치 및 그에 따른 오버헤드가 필요없다! 이는 멤버 함수 호출이 잦은데 상속은 구현하고 싶은 경우에 성능상으로 엄청난 차이를 낸다.

1.5. 용도

본래 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의 대체도 날아간 상황.

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

2010년대에 이르러서는 CUDA의 강력한 존재 덕분에 인공지능 관련 수요가 급격하게 늘어났다.

2. 문법

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

3. 언어 명세

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

4. 표준 라이브러리

파일:상세 내용 아이콘.svg   자세한 내용은 C++/표준 라이브러리 문서
번 문단을
부분을
참고하십시오.
| C++ 표준 라이브러리|<table width=90%><tablealign=center><tablebordercolor=#030303,#eeeeee><nopad>
||<:><width=23%>분류||<-2>이름||
환경 # <cstddef>
# <cstdlib>
# <version>
C++20
# <debugging>
C++26
# <text_encoding>
C++26
오류 처리
# <exception>
# <stdexcept>
# <system_error>
C++11
# <expected>
C++23
메타 프로그래밍 # <type_traits>
C++11
# <concepts>
C++20
리플렉션 # <typeinfo>
# <typeindex>
C++11
# <source_location>
C++20
# <stacktrace>
C++23
숫자 # <complex>
# <bitset>
#
# <linalg>
# <ratio>
C++11
# <numbers>
C++20
# <bit>
C++20
숫자 메타 #
#
#
C++11
#
C++23
# <limits>
문자열 # <string>
# <regex>
C++11
# <string_view>
C++17
# <charconv>
C++17
# <format>
C++20
선형 연속 컨테이너 # <vector>
# <deque>
# <array>
C++11
# <inplace_vector>
C++26
선형 연결 컨테이너 # <list>
# <forward_list>
C++11
연관 컨테이너 # <set>
# <map>
# <unordered_set>
C++11
# <unordered_map>
C++11
컨테이너 인터페이스 # <stack>
# <queue>
# <flat_set>
C++23
# <flat_map>
C++23
순회자 # <iterator>
범위 알고리즘 # <algorithm>
# <numeric>
# <execution>
C++17
범위 인터페이스 # <span>
C++20
# <mdspan>
C++23
# <ranges>
C++20
# <generator>
C++23
스트림 #
#
#
#
#
# <iomanip>
# <iostream>
# <sstream>
# <strstream>
# <syncstream>
C++20
# <spanstream>
C++23
IO # <iostream>
# <fstream>
# <print>
C++23
유틸리티 # <utility>
# <functional>
# <random>
C++11
# <initializer_list>
C++11
# <chrono>
C++11
# <tuple>
C++11
# <variant>
C++17
# <any>
C++17
# <optional>
C++17
# <filesystem>
C++17
# <compare>
C++20
동적 메모리 관리 # <new>
# <memory>
# <scoped_allocator>
C++11
# <memory_resource>
C++17
병렬 실행 # <thread>
C++11
# <future>
C++11
# <coroutine>
C++20
# <generator>
C++23
동시성 제어 # <mutex>
C++11
# <condition_variable>
C++11
# <shared_mutex>
C++14
# <stop_token>
C++20
# <barrier>
C++20
# <latch>
C++20
원자적 연산 # <atomic>
C++11
# <rcu>
C++26
# <hazard_pointer>
C++26
현지화 # <locale>
#
# <text_encoding>
C++23
||


5. 역사

5.1. 모던 C++

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

5.1.1. C++26

C++26 변경사항
{{{#!wiki style="margin: 0 -10px" <colbgcolor=#9FC9BF,#06452A>모듈 설명
<debugging> 40년 동안 개발자들이 직접 구현하거나 각종 IDE에서 직접 마련해주어 중구난방이었던 디버그 환경을 통합하는 모듈. 디버그 모드인지 여부 확인, 그리고 중단점(Breakpoint)의 동적인 생성 및 관리가 가능함
<text_encoding> 현재 실행 환경의 문자 인코딩 정보를 보여주는 모듈 [23]
<inplace_vector>
  • std::inplace_vector<T, Allocator> 기존 <vector>에서 동적인 메모리 크기 변경 기능을 뺀 클래스. 최적화를 위해 배열 처럼 고정된 크기가 필요하지만, 힙에 메모리를 할당할 필요가 있을 때 유용하다. 원래 <vector>를 이런 용도로 장기간 활용해왔으나 이제 표준으로 들어왔다.
<rcu> CRTP & 스레드 멈춤 없는(Non-Blocking) 방식으로 안전한 읽기, 쓰기, 삭제를 할 수 있는 원자적 클래스를 만들고 관리하는 유틸리티를 제공하는 모듈
<hazard_pointer> CRTP & RAII & 스레드 멈춤 없는(Non-Blocking) & 배타적 메모리 소유권(Ownership) 방식으로 하나의 포인터에 대하여 단일 쓰기 / 다중 읽기가 가능한 원자적 연산을 제공하는 모듈
<linalg> <mdspan>을 활용해 선형대수 연산을 제공하는 모듈
<functional>
  • std::copyable_function std::move_only_function과는 달리 복사 생성까지 허용하는 클래스. std::function을 완전한 상위 호환으로 대체할 수 있음
  • std::function_ref 실행가능한(Invocable) 객체의 참조를 저장하는 클래스
}}} ||

5.1.2. C++23

C++23 변경사항
{{{#!wiki style="margin: 0 -10px" <colbgcolor=#FAEE9E,#180809>모듈 설명
함수의 호출 스택을 드러내는 리플렉션 라이브러리
오류 코드 및 예외 방식을 대체하는 클래스. 모나드 연산을 지원한다
불편한 다중 배열을 대체하는 클래스. std::span과는 다르게 실제 데이터를 소유할 수 있다
너무 낡고 저성능인 iostream을 대체하는 출력 라이브러리. 콘솔 뿐만 아니라 FILE 포인터로 출력이 가능하다
클래스에서 스트림 입출력을 지원하는 라이브러리
<flat_map>, <flat_set> 실제로 연관 컨테이너가 아닌 컨테이너에서도 사상 연산을 지원하는데 도와주는 인터페이스 클래스
코루틴을 통해 비동기 범위 연산을 지원하는 클래스. 범위 기반 for문을 비동기적으로 실행할 수 있다
  • std::move_only_function 느리고 위험한 std::function과는 다르게 noexcept를 포함해 함수의 원형을 그대로 보존하고 함수 객체의 빠른 할당을 보장함
  • std::forward_like() 함수의 매개변수를 자료형과는 상관없이 값 범주(Value Category)를 유지한 채 전달을 수행함
}}} ||
호출 스택 추적(리플렉션), executor 등이 추가될 예정이었으나, 전세계적인 코로나 바이러스 판데믹으로 인해 표준 위원회의 작업이 더뎌지고 있다. 주로 다른 언어의 장점을 취합하여 C++20까지의 결점이나 부족했던 기능들 등을 보완하는 패치이다.
  1. 개별 헤더가 아닌 표준 라이브러리 전체에 대해 모듈 지원. import std; 또는 호환성을 위해 import std.compat;으로 사용 가능함
  2. 정적 [] 연산자
  3. 정적 () 연산자
  4. 인덱스 참조 연산자 [] 에 여러 개의 인자를 전달할 수 있음
  5. 람다 표현식에 특성을 사용할 수 있음
  6. constexpr 함수 안에서 goto문을 포함하여 상수 평가식이 아닌 구문을 사용할 수 있음. 왜냐하면 C++23에 와서는 constexpr는 반드시 상수 평가식이 아니라, 컴파일 시간에 실행될 수 있는 표현식이기 때문임. 대신 문맥 구분을 위해 반드시 std::is_constant_evaluated()를 사용해야함
  7. 완벽한 인자 전달 (Perfect Forwarding)을 위해 std::forward 대신에 auto(glvalue), auto{glvalue}를 사용할 수 있음
  8. 새로운 특성 [[assume(expression)]]

5.1.3. C++20

C++20 변경사항
{{{#!wiki style="margin: 0 -10px" <colbgcolor=#F3E5FA,#1a0320>모듈 설명
concept를 활용한 템플릿 제약조건 라이브러리
기존의 >=, >, <, <=를 대체하는 <=> 연산자 지원 라이브러리 [24]
뮤텍스를 사용하는 스트림 인터페이스 라이브러리
문자열 형식화 라이브러리
횟수 세기 기반의 1회용 동기화 클래스
접근하는 스레드를 묶음 단위로 관리하고, 접근하는 스레드를 대기 상태로 만들어 최대 횟수에 도달하면 재시작하는 동기화 클래스
접근하는 스레드의 수를 제한하는 동기화 클래스
코루틴 프레임워크
<source_location> C의 코드 정보 매크로를 대체하기 위한 라이브러리
수학 상수
std::string_view와 같이 동적 할당없는 연속된 메모리 보기를 지원하는 클래스
두가지 목적이 있다. std::ranges에서 컨테이너 오류를 원천 차단하기 위해 제약조건을 꼼꼼하게 달아서 헤더를 대체하는 목적, 그리고 std::views의 범위 대리자 클래스를 통해 동적할당 없이 일반화된 범위[25] 연산을 지원하는 것. 오랜만에 나온 제대로 된 일반화 프로그래밍 라이브러리이기도 하다
}}} ||
2020년에 최종 승인된 C++20은 C++11, C++14, C++17에 이은 주요 버전이다. 비동기 프로그래밍을 지원하기 위한 코루틴[26], 컴파일 속도 향상을 위한 소스 파일 모듈화[27], 템플릿에 제약 조건을 추가하기 위한 컨셉트[28] 다양한 기능들이 추가될 예정이다. 그 외에 추후 표준에[29] 추가될 수 있는 리플렉션과 네트워크 라이브러리네트워크 라이브러리인 asio는 또 밀렸다., 2D GUI 라이브러리, 트랜잭셔널 메모리에 대한 시범 사양도 별도로 공개되어 있다.

놀라운 점은 C++20에 추가된 기능들과 C++20에 다 반영되지 못한 대신 다음 C++ 표준에 포함될 예정인 기능들이 대부분 C++TR2 제안서에 이미 언급되어 있던 사양이라는 특징이 있다. 표준화로서 최소 15년만에 빛을 발하게 된 셈.
  1. 모듈 지원. 표준 라이브러리는 개별 헤더만 지원함
  2. 부동 소수점에 대해 원자 클래스 std::atomic 지원
  3. 노가다를 줄여주는 std::remove_cvref
  4. 컴파일 시간에 힙 메모리 할당과 소멸이 가능함
    1. constexpr std::allocator, std::construct_at, std::destroy, std::destroy_at
    2. constexpr std::vector
    3. constexpr std::string
  5. std::is_constant_evaluated(); 함수를 통해 현재 컴파일 시점인지 아닌지 알 수 있음
  6. 가상, 추상 함수도 constexpr이 될 수 있음
  7. 소멸자도 constexpr이 될 수 있음. 기본 생성자, 소멸자가 모두 constexpr라면 자명 (Trivial)한 클래스가 되고 consteval 함수의 반환 값, 각종 상수 평가식, 그리고 템플릿 매개변수로 사용할 수 있음
  8. constexpr 함수에 try 문을 사용할 수 있음. 만약 상수 시간에 오류가 발생하면 즉시 알 수 있음
  9. consteval로 이젠 모든 것이 다 되는 constexpr 특유의 모호성을 제거하고 반드시 상수 시간에 평가하도록 설정할 수 있음
  10. constinit를 통해 static 변수를 컴파일 시간에 초기화할 수 있음
  11. 함수를 정의할 때 noexcept(상수 bool 평가식)와 같이 선택적으로 noexcept를 지정할 수 있음
  12. <chrono>에서 년월일, 요일, 주말, 날짜, C 라이브러리를 대체하는 시간대 권역 지원
  13. <thread>에서 소멸자에서 자동으로 join()을 실행하는 std::jthread 클래스가 추가됨. 그리고 std::stop_token, std::stop_source를 이용해 스레드 진행을 원격으로 멈출 수 있음 [30]
  14. 새로운 특성 [[no_unique_address]]
  15. 새로운 특성 [[likely]], [[unlikely]][31]

5.1.4. C++17

C++17 변경사항
{{{#!wiki style="margin: 0 -10px" <colbgcolor=#EFFAFD,#08131B>모듈 설명
다양한 타입의 값 하나를 넣을 수 있는 만능타입 클래스
값의 존재 여부를 선택적으로 결정할 수 있는 클래스
타입 안전한 결합체 (union)
<memory_resource> 동적할당하는 방법 자체를 런타임 시점에 결정하는 클래스
<string_view> 메모리를 수정하지 않는 읽기 전용 문자열 클래스
OS수준의 파일관리를 간편하게 접근 (디렉토리 탐색, 파일 복사 및 삭제 등)
병렬 STL 알고리즘
  • enum class std::byte
  • constexpr
}}} ||

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

Visual Studio의 경우 2017 이상, GCC의 libstdc++의 경우 9 이상에서 지원한다.
  1. 템플릿 인자 추론

    1. {{{#!syntax cpp template<typename T> class Aaa {};
template<typename T>
Aaa(T) -> Aaa<T>;}}}와 같이 사용자가 템플릿의 추론 방법을 직접 명시할 수 있음. 가령 std::vector v{1, 2, 3};의 경우 v는 std::vector<int>로 연역됨.
  1. 구조적 바인딩: 튜플 또는 집합 구조체를 풀어쓰는 기능을 제공함. 가령 auto [a, b, c] = std::tuple(4, 5, 6);에서 a는 4, b는 5, c는 6이 됨
  2. 클래스의 객체를 생성하기 위해 생성자에 바로 같은 클래스 객체를 전달했을때, 해당 클래스의 이동 생성자가 존재한다면 순수한 R-Value가 되어 마치 존재하지 않은 것처럼 처리됨. 예를 들어 이동 생성자가 있는 클래스 A를 생성할 때 A aaa{A{}};에서 안쪽의 A{}는 코드 상에만 보이고 실행 시점에는 보이지 않는 존재가 됨
  3. noexcept 속성이 함수의 자료형에 포함됨. 함수 포인터, 람다 표현식, 함자 객체에서 noexcept를 명시한 것과 하지 않은 것이 다른 함수로 취급됨
  4. 람다 표현식의 문맥 캡처 부분에 this를 사용할 수 있음. 이를 통해 자신의 메서드, 필드만 캡처할 수도 있음
  5. constexpr 람다 표현식
  6. if constexpr if문과 사용법은 같지만, 평가되고 실행되는 내용은 컴파일 시점에 즉시 결정됨. 실행되지 않는 쪽은 아예 코드에서 제거됨. 이를 이용해 오버로딩 없이 완전 다른 자료형을 반환하는 함수를 만들 수 있음
  7. inline을 변수 선언에 사용할 수 있음. static inline 필드는 선언 즉시 값을 할당할 수 있음. 어떤 이름공간 안에 있는 inline constexpr 변수는 static 변수처럼 중복 선언이 일어나지 않으며 항상 단일하게 존재하는 이름이 됨. 템플릿 inline constexpr 변수는 늦게 평가되며 컴파일 시점에 값이 그대로 대입됨. 그리고 static constexpr의 경우 자동으로 static inline constexpr로 연역됨
  8. 중첩되는 이름공간을 namespace ns1::ns2 { ...; }와 같이 바로 선언할 수 있음
  9. 이름없는 매개 변수가 가능해짐. 함수에 std::in_place, std::in_place_type<T> 같은 메타 인자를 전달할 때 유용함
  10. Enum en{ value };과 같이 열거형의 생성, 대입을 형변환 없이 바로 할 수 있음. 이는 enum class에도 적용됨
  11. 번거롭게 alignas를 클래스에 붙일 필요 없이 std::aligned_alloc로 동적으로 메모리 할당과 동시에 정렬이 가능해짐
  12. 새로운 특성 [[fallthrough]]
  13. 새로운 특성 [[maybe_unused]]
  14. 새로운 특성 [[nodiscard]], [[nodiscard(message)]]

그외 새로운 기능들은 이곳에 잘 정리되어 있다.

5.1.5. C++14

C++14 변경사항
{{{#!wiki style="margin: 0 -10px" <colbgcolor=#FFFCFD,#110D12>모듈 설명
<shared_mutex> 단일 쓰기 & 다중 읽기 잠금을 지원하는 상호배제 클래스
  • std::tuple와 관련 유틸리티 함수: constexpr로 개선됨
  • std::integer_sequence: 컴파일 시간에 확장되는 정수 수열
  • std::make_unique
}}} ||

2014년 8월에 ISO/IEC 14882:2014라는 정식 명칭으로 C++14이 제정되었다. 표준안 작성 당시 C++0x라 불리었던 C++11처럼 C++14도 C++1y라는 코드네임으로 불렸으며, 주로 C++11에 추가된 요소들을 다듬고 확장하는 데 치중한 비교적 마이너 표준안이다.
  1. 템플릿 변수 #
  2. auto를 사용한 함수의 반환형 추론
  3. auto를 사용한 일반화된 람다 표현식
  4. constexpr 함수에서 static_assert, typedefusing을 사용한 자료형 별칭 선언, using namespace, using type::필드, 메서드, 생성자 등을 사용할 수 있음
  5. 아무것도 안하거나, 아무것도 반환하지 않는 constexpr 함수가 허용됨
  6. 1'000'000'000와 같이 '를 사용하여 자리수를 구분할 수 있음
  7. 0b011101101와 같이 두문자 0b를 붙여 2진수를 사용할 수 있음. '도 사용 가능함

5.1.6. C++11

C++11 모듈 목록
{{{#!wiki style="margin: 0 -10px"
{{{#!folding [ 펼치기 · 접기 ]
<system_error>
<type_traits>
<initializer_list>
<scoped_allocator>
<forward_list>
<unordered_set> <unordered_map>
<conditional_variable>
}}} }}} ||

새로운 기능들이 많이 포함되었던 C++TR1이 정식 표준으로 채택되지 못 해서 대부분의 프로그래머들에게 있어서 C++은 C++98 이후 13년 간 정체된 언어나 다름 없었다. 2011년 8월 12일에 최신 컴퓨팅 환경을 지원하기 위해 급격한 변화의 첫 단추를 끊은 새로운 표준안인 C++11이 ISO/IEC 14882:2011라는 정식 명칭으로 최종 승인되었다. 최종 승인되기 전까지 표준안 개발 당시엔 C++0x라 불리었는데, 2000년대 안에는 표준화가 될 것이라는 의미였다. 하지만 결국 2000년대가 끝나고 2011년 8월에서야 정식 C++ 표준이 되어서 11이 붙었다. # 여담이지만 추가된 많은 기능 중 상당수 항목들이 Boost 라이브러리를 다수 참조했다. 아예 Boost 라이브러리 개발진 중 다수가 C++ 표준화 위원이기도 하다. 차세대 C++에 들어갈 요소를 미리 개발해서 시험해본 것이라 봐도 무방하다.

우측값 참조자를 이용한 이동 연산[34], constexpr, std::initializer_list, 범위 기반 for 문[35], 람다 표현식, 스마트 포인터[36], 정규표현식[37], 멀티쓰레드 등등 중요한 신 개념과 편의 기능들이 많이 추가되었으므로 반드시 확인할 필요가 있다. C++11에 추가된 내용만 적절히 활용해도 코드의 작성과 관리가 보다 명확하고 더 쉬워진다.

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

====# C++TR1 #====
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에서 일부 지원했다. 시범적인 사양은 이 사이트에서 확인할 수 있다.

5.2. 클래식 C++

====# C++03 #====
C++03 모듈 목록
{{{#!wiki style="margin: 20 -10px"
{{{#!folding [ 펼치기 · 접기 ]
<type_traits>
}}} }}} ||

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

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

====# 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의 첫 번째 개정판이 발행되었다.

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

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

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

6. 평가

6.1. 장점

6.1.1. 무비용 추상화

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

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

다른 언어에 밀려 점유율이 조금씩 줄어들고는 있지만, 오래전부터 사용되어왔기 때문에 많은 시스템에서 지원하며, C++로 계속 유지되고 있는 프로젝트가 많다. TIOBE에는 2023년 11월 기준 3위를 차지하고 있다. PYPL의 경우 2023년 11월 기준 4위를 차지하고 있다.
“... 나는 꼭 '보이지 않는'이란 수식어를 붙이는데, 이것은 매우 빈번하게 C++가 가장 아래쪽의 인프라를 구성하는 요소로 존재하기 때문이다. 우리는 C++를 매우 다양한 방식으로 지금 현재 이용하고 있지만, C++가 눈에 직접 보이진 않는다. 어쩌면 'C++ inside'로고를 오래전에 개발했더라면 좋았을 것같다. intel inside라는 인텔의 마케팅 로고를 패러디하고 있는 듯. 우리는 (C++를) 보지 못하고 있다. 우리는 전화통신을 사용하지만 C++를 보지 못하고 있다. 우리는 카메라를 사용하지만 C++를 보지 못하고 있다. 우리는 OS를 사용하지만 C++를 보지 못하고 있다. 우리는 시그널 프로세싱을 사용하지만 C++를 보지 못하고 있다. 우리는 C++를 안 보려고 하는 경향이 있다. 하지만 C++는 매우 빈번하게 모든 것의 하부에 (설령 그것이 Java, C#, Python등 다른 언어로 작성된 것이라고 하더라도) 존재하고 있다.”
- Bjarne Stroustrup Core C++ 2021 :: Bjarne Stroustrup :: Thriving in a crowded and changing world: C++ 2006-2020 [40]
비아르네 스트로우스트루프에 따르면 C++20에 도달한 지금의 C++를 한 마디로 요약하면, “An invisible foundation of "everything"(모든 것의 보이지 않는 기반)”이라고 한다. C++가 드라이버와 같은 시스템의 핵심 구성 요소를 작성하고 유지하는데 사용됨으로써 보이지는 않지만 분명히 존재한다는 것이다.

6.1.3. 수많은 기능

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

6.2. 단점

6.2.1. 높은 학습 난이도

파일:learnC++.jpg
[41][42]
C++11이 발표된 2011년을 기점으로 매 3년마다 개정된 표준을 발표하는 것이 관례화 되면서, 점차 추가되어가는 표준 라이브러리 기능과 문법들을 사용한 코드를 보면 도저히 C언어 기반 언어라고는 생각이 들지 않을 정도로 차이가 상당히 커지고 있다. 그러나 기능이 추가되고 성능이 개선되었지만 그만큼 배워야 할 것은 더욱 늘어나게 되었다. 현재 기능의 방대함은 타의 추종을 불허하기 때문에, 모든 요소를 이해하겠다는 생각은 애초부터 버리는게 좋다. C++로 실무 업무를 보는 개발자들도 절대로 요소 전부를 가져다 쓰지 않고 필요한 부분만 참고해가며 사용한다. 특히 C++11 이후 핵심 요소 중 하나인 우측값 참조는 이해하기 어렵다.

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

C++을 배우려는 초보자가 인터넷에 문의를 해보면 보통 초보자용 1개, 중급자용 1개, 표준 라이브러리 서적 1개 등등등 해서 여러 개 읽으라는 답변들이 많이 달리며, 개중에는 1000쪽이 넘어가는 책들도 있다.[43] 물론 반드시 1000쪽짜리 책으로 공부해야 한다는 건 아니지만 C++ 교재들은 대체로 다른 언어 교재에 비해 두꺼운 편이다. 모던 C++의 새로운 기능을 배우고 싶다면 이들을 자세히 구글링하여 찾고 부족한 내용은 C++ 표준 라이브러리를 정리한 사이트에서 알아보는 것이, 최신의 C++ 기능들을 가장 빠르게 익힐 수 있는 방법이다. 일일이 검색하는 것이 어렵거나 귀찮을 경우 ChatGPT와 같은 생성형 인공지능에게 질문을 직접 해 설명을 듣는 방법을 이용해도 좋다.

6.2.2. 메모리 관리 & 보안

C++는 C언어와 마찬가지로 메모리 관리가 비교적 자유로워서 직접 최적화가 가능한 부분이 많지만 실수하기 쉽고 설계가 어렵다는 단점이 있다. 다른 프로그래밍 언어와 구별되는 가장 큰 차이점이다.

프로그래머들의 평균적인 기량이 뛰어나더라도 사람은 가끔이라도 반드시 실수를 하게 되므로 버그가 발생할 수 있다. 특히 포인터와 참조자와 할당된 메모리 사이에 존재할 수 있는 복잡한 관계를 간과할 수 있는데, 사소해보이는 실수라 할지라도 이미 해제된 메모리 영역에 접근하도록 허용하여 제어 흐름이 탈취될 수 있다. 그래서 이는 단순 메모리 누수와는 다른 그 이상의 보안상 심각한 문제이다.[44] GC 언어처럼 잘못된 참조자가 존재할 수 없도록 가비지 컬렉터가 알아서 처리해주거나, Rust처럼 언어 차원에서 컴파일 시간에 메모리 소유권을 검사하고 강제하는 기능이 없기 때문에, 이 부분은 C/C++으로 개발할 때 언제나 염두에 둬야 하는 폭탄 같은 위험성이라고 볼 수 있다. 최근 미국의 NSA에서도 차기 프로젝트에는 C++ 대신 GC 언어나 Rust를 쓰라고 권장했을 정도이며, 보안의 중요성이 나날히 높아지는 이 순간 C++의 존속까지 위협하는, 가장 큰 역린이라고 할 수 있다. JDNet 가이드라인

스마트 포인터를 강제한다 하더라도 잘못된 참조자로 인한 보안 문제는 피할 수 없다. 그래서 이러한 문제에 대해 지속적으로 논의가 이루어지며 아얘 Rust처럼 컴파일러가 참조자를 검증할 수 있도록 C++ Lifetime Profile이란 제안서가 발간되기도 했다. 메모리 임의 참조를 막아야 한다, nullptr을 아예 불허해야 한다는 프레젠테이션도 C++Con에서 발표된 적이 있다. C++ 개혁 C++ 메모리 안전

6.2.3. 애매한 위치

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

리누스 토르발스의 발언을 인용한 C++ 비판에 대한 비아르네 스트로우스트루프의 가장 최신 반응은 '그것은 기껏해야 고대의 C++에 대한 비판일 뿐 모던 C++에 적용되는 얘기가 아님.'[45]으로 요약할 수 있다.
"... 사람들은 매우 자주 C++를 잘못 묘사하곤 한다. 사람들은 1984년 근처의 C++를 바라보고 비판을 하곤 하거나, 25년 전[46]의 비판을 발굴해내서 무슨 복음말씀처럼 여기곤 한다. 심지어 나는 오늘 아침에도 리누스가 1990년대 초에 C++에 대해 언급한 코멘트를 레퍼런스로 하는 것을 보았다. 그 당시의 g++ 컴파일러라면 나도 싫다. 하지만 그건 수십년 전이고, 세상은 25년 전과는 많이 달라졌다."
In fact, in Linux we did try C++ once already, back in 1992.
사실 1992년에 이미 리눅스에 C++를 사용하는 것을 시도해보았다.
It sucks. Trust me - writing kernel code in C++ is a BLOODY STUPID IDEA.
구리더라. 커널을 C++로 짜는 것은 정말 개 멍청한 생각이다.
The fact is, C++ compilers are not trustworthy. They were even worse in 1992, but some fundamental facts haven’t changed:
사실 C++ 컴파일러는 신뢰할 만한 것이 못 된다. 1992년에는 더 심각했지만 몇몇 핵심적인 사실은 변하지 않았다:
the whole C++ exception handling thing is fundamentally broken. It’s especially broken for kernels.
C++ 예외 처리는 그냥 처음부터 글러먹었다. 특히 커널을 위해서는 더 그렇다.
2004년 리누스 토발즈의 C++ 평가 #
그러나 비아르네 스트로우스트루프의 평과와 달리 리누스 토발즈는 1990년대를 시작으로 2000년대와 2020년대 까지 꾸준하게 C++를 비난하고 리눅스 커널에 C++ 도입을 반대했다. 정작 역사가 더 짧은 Rust 도입은 찬성 #한걸 생각하면 여전히 C++의 문제점이 해결되지 않아 신뢰하지 않음을 알수있다.

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

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

다만 C언어와 호환된다고만 했지 섞어 쓰는 것은 권장되지 않는다. 오히려 두 언어가 섞이면 관리가 힘들어지고 버그나 예상하지 못한 결과가 나올 수도 있다. C와 C++는 완전히 다른 별개의 언어이며, 웬만하면 섞어 쓰지 말자. [47]

C언어의 #include문은 다른 언어의 using, import, package와는 결이 다르다. 지정한 파일의 내용을 가감없이 그대로 포함시키는 구문이다. 그 안에 있는 모든 게 상호작용되고 특히 단순 치환하는 매크로 때문에 코드 간 의존성이 발생하여 미리 컴파일할 수 없으며 해당하는 헤더 파일을 컴파일 할 때마다 일일이 포함해야 한다. 원치 않으면 헤더와 소스 파일을 분리해야 한다. 헤더 참조 구조가 중복정의로 복잡해지는 건 덤. 그 결과 C/C++는 컴파일이 가장 느린 언어가 되어버렸다. 이러한 문제를 해결하기 위해 C++20부터 모듈 개념이 도입되었다 [48]. 다른 언어의 패키지, 모듈처럼 C++20의 모듈도 외부와 단절되어 있고 오직 가져오기 구문으로만 참조할 수 있다. 그러므로 이러한 단점은 시간이 지남에 따라 어느 정도 극복될 것이다.

그런데 다른 모듈에서 정의된 매크로는 사용하지 못하기 때문에, 매크로로 떡칠된 코드는 다 뜯어 고쳐야 할 수도 있다. 그래서 수많은 기존 프로젝트는 여전히 헤더를 사용할 것으로 보이며, C와의 호환성을 위해서 헤더 참조 구조는 표준에서 없어지지 않을 것이다. 또한 모듈을 모든 C++ 컴파일러에서 지원하는게 아닐 뿐더러, 모듈을 지원하거나 사용하는 라이브러리가 아직은 전무하다. 아니면 이 상황에서 MSVC, GCC, Clang, ICC와 같은 메이저 컴파일러들은 헤더를 프리 컴파일하고 사용하는 확장 기능들을 제공하고 있으므로 이를 사용하는 것이 최선이다.
6.2.3.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와 같은 사실상 꼼수에 가까운 기법들이 사용되고 그 중 템플릿 메타 프로그래밍 또한 이에 포함된다. 여기서 말하는 메타 프로그래밍은 컴파일 타임에 실행되는 프로그램이라는 넓은 의미를 가진다. 템플릿은 원래 일반화 프로그래밍을 위해 등장했으나, 템플릿 타입 체크 등의 몇 가지 케이스들을 보완하기 위해 여러 기능들이 추가되기 시작했다. 이러한 기능들은 SFINAE[49]와 같은 트릭에 사용되다가, 어느 때부터 C++ 템플릿을 활용하여 튜링 완전한 메타 언어로써 연산을 컴파일 타임에 수행할 수 있음을 발견했다. 이러한 흐름에서 등장한 템플릿 메타 프로그래밍은 C++의 강력한 기능 중 하나가 되었지만 원론적으로는 언어 표준 명세에서 지원하지 않았다. 문법 관계가 복잡하고 직관적이지 않아 이해하기가 어렵고 컴파일도 오래 걸리며 부자연스럽다거나 난해하다는 의견이 적지 않다. [50]개발자들이 C++에서 손꼽는 어려운 특징 중 하나이다. # # SFINAE가 수행했던 타입 체크는 C++20에서 Concept를 도입하여 템플릿에 명시적 조건을 추가할 수 있게 됨으로써 의미가 없게 되었다.[51]

6.2.4. 성능 저하 요소

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

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

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

현재 RTTI는 유행이 많이 지난 기능이며, 최신 C++ 프로그래밍에서는 잘 사용하지 않는다. 동적 타이핑은 꼭 필요할 때만 사용하는 것이 대세이며, 이외에는 다형성을 구현하기 위해서는 CRTP + type erasure 등의 기법을 주로 사용하며, 이는 성능 패널티가 없다.
6.2.4.2. 예외
예외도 마찬가지로, 설계상 예외 핸들러를 찾기 위해 (RTTI와 동일한 맥락으로) 던져지는 예외의 유형별로 형변환을 해야 하기 때문에 런타임 타입 정보가 필요하다. 따라서 예외를 던질 때마다 적지 않은 성능 패널티가 부과된다. 심지어 예전에는 try 블럭을 설치하는 것과, 소멸자가 있는 클래스를 정의하는 것만으로도 프로그램 성능이 크게 하락했었다. 최신 컴파일러는 zero-cost exception[53]을 사용하기 때문에 예외가 발생하기 전까지는 성능 하락이 발생하지 않는다. if문 처리는 분기 예측에 따라 1~20 cpu cycle를 소모하지만, 예외 처리는 5000~10000 cpu cycle를 소모한다는 점을 알아야 한다. 따라서 예외 처리를 남용해서는 안되며 성능이 중요한 부분에서는 사용하지 않는 것을 권장한다. #

예외는 수많은 프로그래밍 언어의 핵심 오류 매커니즘이다. 하지만 이런 상당한 단점이 있게 되자, 대규모 프로젝트에서는 성능 상의 이유로 예외의 사용을 금지하게 되고[54]. C++17의 filesystem 라이브러리의 경우 예외 처리와 에러코드용 인터페이스를 혼용해서 제공해버릴 정도로 C++의 예외 처리는 처리 비용이 상당하다.

RTTI와 달리 C++의 표준 라이브러리를 사용하고 오류 처리를 동반하는 한 예외는 현실적으로 피할 수 없다. C++에서 객체의 생성자를 실패시킬 수 있는 문법적 기능은 예외가 유일하며 표준 라이브러리는 이를 적극적으로 활용하기 때문이다 [55]. 물론 처리하기 전에 앞서 확인하는 과정을 통해 피할 수 있는 예외는 당연히 피하는 것이 좋다. 예를 들어 std::optional에 유효한 객체를 가지고 있지 않을 때 객체에 접근하여 발생하는 std::bad_optional_access 예외는 접근하기 전에 미리 확인하여 피할 수 있다. 그러나 컨테이너 라이브러리, new 등을 사용하는 과정에서 발생하는 std::bad_alloc과 같이 메모리 할당의 실패로 발생하는 예외는 남은 메모리 크기를 확인하더라도 피하지 못할 수 있다.[56]

메이저 컴파일러들 중 일부는 RTTI와 마찬가지로 예외 처리를 끄는 기능을 제공하긴 한다. 하지만 이 경우 표준 라이브러리를 사용하는 중 거기서 예외를 던지는 순간 정의되지 않은 동작(undefined behavior)으로 돌입한다. 컴파일러나 런타임 라이브러리에 따라 다르긴 하지만, 대부분의 경우 프로그램이 종료된다.

이렇게 성능 상의 문제로 예외 처리를 기피하는 상황이 끊임없이 발생하자, 아예 오류 코드 방식을 확장시킨 Boost.Outcome 혹은 <expected> 등으로 대체하려고 하고 있다. 이러한 개념을 먼저 도입한 언어의 예로 Rust KotlinResult를 들 수 있다. Rust는 예외 대신 Result<T, E> 객체와 열거형 [57]을 사용하며, From 트레이트가 구현되어 있는 경우 ? 연산자에서 자동 변환을 지원한다. 그 외에 동적 형변환을 포기하고 에러 코드와 예외의 장점을 섞은 표준안이 제안되고 있으나 갈 길이 멀다. #

6.2.5. 패키지 관리자

Rust와 비교했을 때 메모리 안전성 외에 가지게 되는 또다른 상대적인 단점인데, 러스트는 Cargo라는 성능 좋은 패키지 관리자가 공식 지원되는 데 반해 공식적인 패키지 매니저가 없다. 다만 공식적인 패키지 관리자가 없다 뿐이지 무료로 사용 가능한 여러 패키지 관리자가 나와 있는데, 사람들이 가장 많이 쓰는 건 마이크로소프트에서 만든 vcpkg이고, 그 다음으로 많이 쓰이는 건 Conan이다. Conan은 특이하게도 스크립트로 Python을 사용한다.
파일:conan2-logo-for-light.svg 파일:conan2-logo-for-dark.svg
Conan의 로고 이미지

진짜 문제는 이들이 Java의 Gradle 또는 Maven, Python의 pip, Node.js의 npm 등 정도의 인지도를 가지고 있지 않다는 것.

7. 학습 자료

학습 로드맵 겸 영어 학습 자료

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

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

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

게다가 애시당초에 번역을 하면 안되는 멀쩡한 코드 예제까지도 편집 과정에서 잘못 건드리는 경우도 있어, 예제 그대로 타이핑해서 실행해도 컴파일 에러를 뿜어내는 경우도 있다. 이런 문제의 경우 사실 어느정도 문법에 익숙하다면 금방 해결 가능한 사소한 수준의 문제라 뇌내보완이 가능하나, 문제는 이런 기초 입문서를 배우는 초심자로써는 사소한 오탈자로 인한 에러도 잡아내기 어렵다는 것이다.

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

아래의 도서들은 한국어 번역본이 존재하는 도서를 우선시했다.
<colbgcolor=#fff><colcolor=#000> 입문 & 초급 입문용 서적으로는 외국의 입문서라고 하면 Teach Yourself 시리즈나, Primer 시리즈 등이 가장 처음 볼 책으로 추천되곤 한다. 다만 C++의 분야가 원체 방대한데다 보통 초심자용 책은 상세히 풀어쓰는 경향 때문에 분량이 늘어나는 특성이 있기 때문에 둘 다 엄청나게 두꺼운 편이며, 예제나 서술 방식에 호불호가 크게 갈리는 책이다. 한국인 저자의 책이라면 대학교재로 많이 쓰이는 명품 C++ programming 책이 있다.
C++ 창시자인 비아네 스트로스트룹 본인이 학부생 수업 교재로 사용할 목적으로 쓴 Programming: Principles and Practice using C++는 보통 컴퓨터 관련 학부 2학년에 존재하는 프로그래밍 방법론 등의 이름을 가진 특정 언어와는 무관한 수업의 교재이지만, C++ 입문서로도 꽤 유용하다.
오래된( 2005년) 책이지만 Effective C++(3판)을 보는 것도 좋다. 번역이 매끄러워 읽기 좋고 이해하기 쉽다. 이 책은 입문용 책이 아니라 기초를 모두 알고 있는 사람이 읽으면 좋은 책으로 C++의 기초를 이해하고 있다면 C++의 STL 에 대한 책을 같이 병행하는 것이 좋다. 한국인 저자의 책이라면 뇌를 자극하는 C++ STL이 있다.
한국인 저자가 쓴 책으로 두들낙서의 C/C++ 한꺼번에 배우기가 있다. 저자의 유튜브 채널에서 동영상 강의를 무료로 볼 수 있다.
인터넷으로 배우고 싶다면 모두의 코드를 추천한다. 위키독스에서 C++ 이야기(A Story of C++)를 볼 수도 있다. 영어에 능숙하다면 LearnC++도 좋은 선택이다.
대부분의 C++ 책들이 C언어를 배웠다는 전제로 쓰여있는데, C언어를 안 하고 바로 C++를 하고 싶다면 그림으로 배우는 C++ Programming 2nd Edition 이 책을 보면 된다.
C언어 없이 C++를 바로 시작하고 싶고 상세한 설명을 원한다면 포르잔 C++ 바이블을 읽는 것이 좋다. 파이썬 교재로 유명한 혼자 공부하는 파이썬의 저자가 번역한 책이라 번역의 질이 보장이 된다는 게 장점이다. 다만 대학 교재용으로 나왔다보니 연습 문제의 해답이 제공되지 않는다는 단점이 있지만[59] 구글에 검색을 해보면 블로그에 개발자들이 올려놓은 해답을 찾을 수 있다. 1000페이지가 넘어가는 두께에 질렸다면 축약본인 포르잔 C++ 에센셜도 있는데 이 책의 역자는 자신의 유튜브 영상 댓글에서 혼자 공부하는 사람은 바이블을 읽을 것을 권장했다.
C 초급용 추천서적 5종, C++ 초급용 추천서적 5종
중급 일단 초급용 서적을 한 권 독파하고 나면, Effective Modern C++, C++ Template: The Complete Guide, C++ Concurrency in Action 정도의 책을 권하는 사람이 많다. 특히 스콧 마이어스의 Effective Modern C++는 거의 필독서에 가깝다.[60]
C언어의 창시자가 쓴 The C Programming Language가 초급자들부터 봐야 하는 책으로 유명한 것에 비하여, C++의 창시자 비아네 스트로스트룹의 The C++ Programming Language는 절대 초급용 서적이 아닌, 중급 이후의 레퍼런스 서적이다.
이후 이 단계까지 성공적으로 도달했으면, 최종 목표에 따라서 책을 보아야 하는 단계이므로 더 이상 일반적인 추천이 가능하지 않다.

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

8. 방언 및 확장

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

8.1. GPGPU를 위한 확장

8.1.1. CUDA

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

8.1.2. OpenCL

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

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

8.2.1. C++/CLI

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

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

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

8.2.2. C++/CX

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

8.2.3. C++/CL

공식적인 명칭은 아니다. 하지만 Visual Studio의 C++ 컴파일러는 C++ 표준에도 없는 문법을 지원하거나 C++표현에서 아예 명시적으로 금지된 문법까지 지원하고 있다. 예를 들자면 어떤 struct 또는 union안에 익명 struct/union을 만들고 이 멤버에 접근하는 것은 ISO C++ 구현에서 명시적으로 금지되어 있지만[ISO/IEC][65] Visual Studio cl.exe는 이런 표준 위반을 상관하지 않는다.
이러한 이유로 GCC/Clang에서 컴파일 되는 코드는 MSVC에서 컴파일 되지만 MSVC에서 컴파일 되는 코드는 GCC/Clang에서 컴파일이 되지 않거나 경고를 내는 경우가 잦다.

9. 관련 문서

10. 외부 링크



[1] 덴마크인이라 이름 역시 덴마크어이며, 북유럽권 언어가 그렇듯이 영미권에서도 상당히 난해하게 들리는 이름이다. 스트로스트룹이라는 이름은 그냥 알파벳 철자대로 영미식 발음으로 읽은 것이라 원래 발음과는 상당히 다르다. 이름 발음을 물어 보는 경우를 하도 많이 들었는지 아예 본인이 자기 사이트에 wav 파일로 이름 읽는 법을 올려두었다. 대한민국 웹 역시 그냥 영미권 발음대로 적거나 아예 음역조차 하지 않고 알파벳 그대로 적어두기도 한다. [2] 간단하게 'C=C+1' 정도 의미다. 수학교육과 등에서는 '다음수'라는 개념으로 가르친다. [3] 그러나 실상 C++11 이후의 모던 C++ 기능을 가르쳐주는 곳이 매우 드물어서 C++의 객체 지향만 조금 배울 수 있다. C++의 객체 지향은 다른 언어에 비해서 좀 더 복잡하다. 제대로 다루기 위해서는 별도의 서적을 참고하는 것이 권장되며, 실제 프로젝트의 코드에 대해 탐구하고 경험해보는 것이 좋다. C++를 모르면 2학년부터는 강의를 전혀 이해할 수 없기 때문에 의외로 많은 학생들이 중도포기를 하게 되며 컴공을 나왔는데도 프로그래밍을 전혀 못하는 학생들이 수두룩하게 발생하고 있다. [4] 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'라는 초기 명칭과는 한참 차이가 날 정도로 확장되었다. [5] 다만 C에서 C++의 방법론을 따라가지 못하는 건 아니다. GNOME의 핵심 라이브러리인 Glib이 대표적으로, C 언어로 객체 지향을 짜도록 구성되어 있다. incomplete type declaration 트릭을 이용하여 캡슐화를 흉내내고, 매크로와 컴파일러 확장을 이용해서 type-generic function을 만들어 쓰고, struct hack을 이용하여 vector를 흉내 내는 식이다. 그러나 저런 트릭은 언어 차원에서 정식으로 제공한다기보다 말 그대로 '트릭'에 가까우므로 이상한 조건들이 붙는 경우가 종종 있고, 그것들을 정확히 파악하고 있지 않으면 상당히 찾아내기 어려운 에러를 내는 경우도 많다. [6] C99에서 추가된 지정 초기자(Designated Initializers) [iostream] [stdio.h] [9] 시간 제한이 빡빡한 PS(문제풀이) 쪽에서는 거의 매번 쓰일 정도인 기능이지만 Visual Studio에서는 의미 없으니 참고. # [10] Smalltalk에서는 모든 클래스는 최상위 클래스를 상속받으며, 클래스가 가진 모든 메타 정보를 어디에서나 접근할 수 있다. Smalltalk의 클래스는 편의상 자료형으로 취급되지만 사실 클래스도 어떤 객체로부터 생성되는 인스턴스다. 클래스에 대해 메타 정보를 서술하는 메타클래스가 존재하며, 이를 외부 인터페이스로 내보이는 Reflection을 구현한다. 즉 Smalltalk의 클래스는 네이티브하게 런타임 인스턴스의 속성만 정의해놓은 것이 아니라, 클래스도 new GameObject 마냥 생성되는 인스턴스일 뿐이다. [11] 다만 해당 언어들조차 파일 입출력에서는 별도의 소멸자, 파괴 메서드, 예외 처리의 도움이 필요하다. [12] 원본 객체가 필요없는 경우, 새로 객체를 생성할 때 깊은 복사 없이 기존 객체의 메모리를 재활용할 수 있도록 하는 기능이다. [13] 또한 레퍼런스 카운팅으로 인해 발생하는 std::shared_ptr의 순환 참조 문제를 피하기 위해서 참조만 할 뿐 소유권은 지니지 않는 std::weak_ptr도 지원한다. std::weak_ptr은 객체 대신 std::shared_ptr을 참조한다고 보면 된다. 하나의 std::shared_ptr과 다수의 std::weak_ptr을 사용하면 여러 소유권자가 하나의 객체를 공유하면서도 순환 참조 문제를 해결할 수 있다. [14] '클래스의 인스턴스'가 아니다. [15] 변수 [16] 자료형 [17] 메모리 정렬 방식, 사용자가 접근할 수 있는 메모리 영역, 바이트 크기, 메서드의 이름과 코드, 필드의 이름과 정보 [18] 이를 '결정적'이라고 한다 [19] 이를 '비결정적'이라고 한다 [20] Functor, Niebloid, Function Object. () 연산자 오버로딩을 통해 함수 인터페이스를 구현한다 [21] GCC와 같은 컴파일러는 최적화 옵션을 넣지 않을 경우 constexpr 함수를 컴파일 시점에 계산하지 않는데, 아래와 같이 템플릿을 섞어서 사용하면 확실히 컴파일 시점에 계산된다. [22] C#을 스트립트 언어로 쓰는 유니티 엔진도 내부 소스는 C++로 이루어져 있다. 즉, 스크립팅용으로만 C#을 쓰는 셈이다. 심지어 C#으로 짜놓은 코드를 C++로 변환하는 IL2CPP라는 기능도 제공한다. [23] , <iostream>에서 활용할 수 있다 [24] C#의 CompareTo()와 같다. [25] begin(); end();를 지원하는 클래스 [26] 언어 차원에서 지원하기 때문에, 비동기 프로그래밍을 위해 별도의 라이브러리가 필요하지 않게 된다. [27] 소스 파일이 각각의 헤더 파일을 일일이 포함하는 대신, 미리 컴파일된 모듈을 사용함으로써 컴파일 속도가 크게 향상된다. 별도의 시범 사양으로도 공개되어 있기 때문에, Visual Studio의 C++ 컴파일러와 Clang 컴파일러에서 이미 시범적으로 지원하고 있다. [28] 현존하는 템플릿 트릭의 대다수를 대체할 수 있으므로 코드가 간결해지고 가독성이 증가한다. [29] 3년 주기를 따를 경우 C++23이 된다. [30] 다른 언어에서 보이는 Cancellation\*류의 클래스다. [31] 분기 예측으로, 리눅스 커널을 공부해본 사람은 아닌 그 likely(), unlikely() 매크로와 유사하다 [32] C++11 제정 당시에도 std::auto_ptr 등 많은 옛날 요소들이 deprecated로 명시되어 사용이 자제될 것을 권장받았으나, 이는 어디까지나 권고라 사용자가 맘 놓고 쓰려면 쓸 수 있었다. 이전 버전의 기능을 아예 죽여버린 사례는, C++98 시절에 실상 누구도 안 썼고 지금은 아예 용도가 바뀌어버린 auto 등 극소수만이 해당한다. [33] C/C++의 특수기호는 미국의 부호체계인 ASCII를 기반으로 하고 있기 때문에 해당 영역에 다른 문자를 넣어 사용하던 유럽국가들의 코드페이지에서는 이상하게 표현되는 일이 있었다. 그래서 잘 사용하지 않는 문자의 조합으로 특수문자를 표기하는 방법. 문자 2개의 조합이면 digraph, 3개면 trigraph. 유니코드가 대세가 된 지금은 사실 필요 없고, 옛날 소스라고 해도 간단한 일괄 변환작업으로 새 표준에 맞출 수 있기도 하다. 다만 표준에서 삭제처리를 할 경우 해당 문법이 다른 기능으로 대체될 수 있기 때문에 일괄변환이 언제나 될 것이라는 보증이 안 되고, 후술하는 IBM 등 장기간의 낡은 코드와 관련된 이해관계자들 입장에서 떨떠름한 것은 사실이다. [34] 객체의 복사에서 원본이 불필요한 경우 이를 활용하면 실행 시간 감소의 효과가 있다. 복사 생성자를 통해 깊은 복사를 수행하는 대신에 원본의 내용을 원하는 메모리에 그대로 전달한다. [35] 반복자 대신 이를 대신 사용함으로써 반복문을 간략하게 작성할 수 있다. [36] 기존 표준의 유일한 스마트 포인터였던 std::auto_ptr은 명시적이지 않은 소유권 이동, 동적 배열의 삭제 불가 등 실무에서 절대 사용하면 안되는 쓰레기였지만, std::shared_ptr와 같은 새로운 스마트 포인터가 추가되면서 메모리 관리가 보다 안전해지고 편해졌다. 스마트 포인터를 잘 활용한다면 메모리 관리에 대한 수고를 덜 수 있다. 실제로 Chromium과 같은 대규모 프로젝트에서 심심치 않게 볼 수 있다. [37] 다만, 속도가 Python보다도 1.5배나 느리다. 컴파일러들의 구현이 좋지 않거나 운영체제의 메모리 할당이 너무 잦기 때문으로 보인다. 자주 사용한다면 별도의 라이브러리를 사용할 것을 추천한다. [38] C++뿐 아니라 C, 파스칼 등 클래식 언어들이 전부 ISO에서 표준화되고 있다. 아예 이런 언어를 표준화하는 전문 태스크 팀 JTC1/SC22라는 게 있으며, 이중 C++는 21번째 그룹에 속한다. [39] 다만 해당 벤치마크의 코드는 언어의 성능을 보여주지 못한다. 그 예시로 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의 성능이 더 잘 나오게 된다. [40] 1:11:14부터 해당 발언 시작 [41] 35040분 = 584시간 = 24일 8시간 [42] 해당 짤에서 나오는 썸네일을 쓰는 실제 유튜브 영상은 사실 1시간 짜리지만, 별개로 31시간 짜리 영상이 있긴 하다. [43] Bjarne Stroustrup이 직접 쓴 TC++PL이 1,300쪽이다. 물론 이 책은 레퍼런스용 책이기 때문에 TC++PL의 서문에는 아예 'C++를 시작하는 입문자에겐 이 책을 권장하지 않는다'는 말이 적혀 있다. [44] IT 업계에서 최고 수준의 시니어 프로그래머들을 많이 보유하고 있는 구글조차도 웹브라우저 엔진인 블링크의 메모리 접근 위반과 관련한 보안 취약점이 해마다 수십 건씩 계속 발견되고 있다. 모질라도 결국 이러한 문제를 장기적으로 해결하기 위해 차세대 엔진인 서보를 후술할 Rust로 작성할 정도이다. [45] Core C++ 2021 :: Bjarne Stroustrup :: Thriving in a crowded and changing world: C++ 2006-2020(1:12:36부터 해당 발언 시작) [46] 1990년대 중반 [47] Jens Gustedt, Modern C [48] MSVC에서는 #include 전처리기 구문을 사용하더라도 모듈로써 동작하도록 설정할 수 있다. [49] Substitution Failure Is Not An Error. 조건에 맞지 않는 템플릿 인자가 들어오면 그 템플릿을 실체화시키지 않는다는 트릭. [50] 이 부분을 언어의 설계 단점으로 보긴 어려운데, 애초에 TMP 은 C++ 에서 공식적으로 지원하는 개념이 아니기 때문이다. 예로 들자면 한 회사(C++)에서 와플 기계(템플릿)를 출시했는데, 사람들이 와플 말고도 여러가지 재료를 넣어서 다른 결과물(TMP)을 만들 수 있는 방법을 찾았다고 하자. 그런데 와플 기계에 부적합한 재료를 넣어 결과물이 타거나 맛 없어 진다고 해도 이 때문에 와플 기계를 개발한 회사를 비판한다는 것은 자연스럽지 않다. [51] 대부분 프로그래머에겐 의미가 없지만, 컴파일러 제작자에게는 의미가 있다. Concept의 구현은 SFINAE 기반이기 때문이다 [52] dynamic polymorphism을 원한다면, 쓰기 싫어도 무조건 쓸 수 밖에 없다. [53] 예외를 던지지 않는 실행경로에서는 아무 것도 하지 않아도 되도록 만드는 기법. 요약하면 컴파일러가 전역 예외 핸들러 테이블을 미리 만들어두어, 예외를 던지면 런타임에 해당 테이블을 참조하여 처리하는 방법이다. 이를 구현한 방법은 주로 SEH(MSVC 런타임)와 DWARF(GCC 런타임)가 사용된다. 저 기법이 등장하기 이전까지는 SJLJ를 사용하여 예외처리를 구현했다. [54] P0709에 따르면 무려 52%의 프로그래머가 프로젝트 내에서 예외가 금지되었다고 답했다. [55] two-phase construction 등 회피 꼼수가 있으나 어디까지나 꼼수다. C++ 특성상 예외를 완전히 피하면서 깔끔한 코드를 설계하는 것은 매우 어렵다. [56] std::set_new_handler 함수를 사용하여 메모리 할당 실패 시 호출되는 핸들러를 변경하여 예외 대신 nullptr로 반환하도록 만들 수 있다. [57] Rust의 열거형은 C++의 <variant>다. [58] 시스템, 실시간 임베디드, 게임, 파이낸스, 그래픽, 머신러닝, 영상처리, 수치 시뮬레이션... [59] 대학 교수, 조교에게만 제공이 된다고 한다. [60] 하지만 한국어판은 번역 오류가 많아 이해가 어려울 수도 있다. [61] 줄여서 Managed C++라고도 불렀다. [62] 게임은 일반적으로 생산성이 매우 중요한 거대 프로젝트이면서 하드웨어의 성능을 극한까지 끌어내야 하는, 속도와 효율에 매우 민감한 특수한 소프트웨어 분야이기 때문에 C++가 많이 사용된다. 괜히 이런 이야기에 게임에 대한 언급이 잘 나오는 게 아니다. [63] 초기의 Managed C++은 언어의 포지션도 상당히 애매했다. 간단히 말하자면 닷넷 프레임워크와 레거시 운영체제 프레임워크를 동시에 지원하고자 했으나 결과는 이도저도 아니었다. 차라리 닷넷 프레임워크가 COM을 지원하니 그런 걸 쓰는 게 낫다. [ISO/IEC] JTC1 SC22 WG21 N 4860 §11.5.1 Anonymous unions [65] 해당 표현은 C11에서는 표준이나 C++는 아니다. [66] 대부분의 컴공과에서 배운다. 많은 컴공과에서 첫 언어로 C를 가르치는 관계로, 학생들에게 익숙한 C의 문법을 가지고 객체 지향 프로그래밍의 개념도 다룰 수 있다는 장점이 있기 때문이다. Java나 Python을 처음으로 배우는 학교의 경우에도 언젠가 한번쯤은 C나 C++를 건드릴 일이 생기게 된다. [67] C++의 슈퍼셋이며, 본 사이트에선 C++의 후계자라고 설명한다. Java에서 Kotlin으로, 자바스크립트에서 타입스크립트로 바뀐 것과 비슷하다고 한다.