Storage class specifiers
저장 클래스 지정자는 이름의 decl-specifier-seq 의 일부입니다. 선언 구문 . 이름의 범위 와 함께, 이들은 이름의 두 가지 독립적인 속성인 저장 기간 과 링크 을 제어합니다.
목차 |
저장 기간
저장 기간 은 객체를 포함하는 저장 공간의 최소 잠재적 수명을 정의하는 객체 의 속성입니다. 저장 기간은 객체를 생성하는 데 사용된 구성에 의해 결정되며 다음 중 하나입니다:
- static 저장 기간
|
(C++11부터) |
- 자동 저장 기간
- 동적 저장 기간
정적 , 스레드, (C++11부터) 그리고 자동 저장 기간은 선언 에 의해 도입된 객체들과 임시 객체들 과 연관됩니다. 동적 저장 기간은 new 표현식 으로 생성된 객체들이나 암묵적으로 생성된 객체들 과 연관됩니다.
저장 기간 범주는 참조에도 적용됩니다.
subobjects 와 참조 멤버들의 저장 기간은 그들의 완전한 객체의 저장 기간과 동일합니다.
지정자
다음 키워드는 storage class specifiers 입니다:
|
(C++11 이전) |
|
(C++17 이전) |
- static
|
(C++11부터) |
- extern
- mutable
decl-specifier-seq 에서는 최대 하나의 저장 클래스 지정자만 사용할 수 있습니다 , 단 thread_local 는 static 또는 extern 과 함께 사용될 수 있습니다 (C++11부터) .
mutable 는 저장 기간에 영향을 주지 않습니다. 사용법에 대해서는 const/volatile 를 참조하십시오.
다른 저장 클래스 지정자는 다음 선언들의 decl-specifier-seq 에 나타날 수 있습니다:
| 지정자 | 다음의 decl-specifier-seq 에 나타날 수 있음 | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 변수 선언 | 함수 선언 |
구조적 바인딩 선언
(since C++17) |
|||||||
| 비멤버 | 멤버 | 비멤버 | 멤버 | ||||||
| 비매개변수 | 함수 매개변수 | 비정적 | 정적 | 비정적 | 정적 | ||||
| auto | 블록 범위에서만 | 예 | 아니오 | 아니오 | 아니오 | 아니오 | 아니오 | 해당 없음 | |
| register | 블록 범위에서만 | 예 | 아니오 | 아니오 | 아니오 | 아니오 | 아니오 | 해당 없음 | |
| static | 예 | 아니오 | 정적 선언 | 네임스페이스 범위에서만 | 정적 선언 | 예 | |||
| thread_local | 예 | 아니오 | 아니오 | 예 | 아니오 | 아니오 | 아니오 | 예 | |
| extern | 예 | 아니오 | 아니오 | 아니오 | 예 | 아니오 | 아니오 | 아니오 | |
익명 공용체 는 static 으로도 선언할 수 있습니다.
|
register 는 해당 변수가 자주 사용되어 CPU 레지스터에 그 값을 저장할 수 있음을 나타내는 힌트입니다. 이 힌트는 무시될 수 있으며, 대부분의 구현에서 변수의 주소가 사용되면 무시됩니다. 이 용법은 더 이상 사용되지 않습니다. |
(until C++17) |
정적 저장 기간
다음의 모든 조건을 만족하는 변수는 static storage duration 을 가집니다:
- 이것은 네임스페이스 범위 에 속하거나 static 또는 extern 으로 처음 선언됩니다.
|
(since C++11) |
이러한 개체들의 저장 기간은 프로그램이 실행되는 동안 지속됩니다.
스레드 저장 기간thread_local 로 선언된 모든 변수는 스레드 저장 기간 을 가집니다. 이러한 개체들의 저장 공간은 해당 개체들이 생성된 스레드의 수명 동안 지속됩니다. 각 스레드마다 별개의 객체 또는 참조가 존재하며, 선언된 이름을 사용하면 현재 스레드와 연관된 개체를 참조하게 됩니다. |
(C++11부터) |
자동 저장 기간
다음 변수들은 automatic storage duration 을 가집니다:
- 블록 범위 에 속하며 명시적으로 static , thread_local , (C++11부터) 또는 extern 으로 선언되지 않은 변수들. 이러한 변수들의 저장 공간은 해당 변수가 생성된 블록이 종료될 때까지 유지됩니다.
- 매개변수 범위(즉, 함수 매개변수)에 속하는 변수들. 함수 매개변수의 저장 공간은 해당 매개변수의 소멸 직후까지 유지됩니다.
동적 저장 기간
프로그램 실행 중 다음 방법으로 생성된 객체는 dynamic storage duration 을 가집니다:
- new 표현식 . 이러한 객체의 저장 공간은 할당 함수 에 의해 할당되고 해제 함수 에 의해 해제됩니다.
- 암시적 생성 을 통한 방법. 이러한 객체의 저장 공간은 기존 저장 공간의 일부와 중첩됩니다.
- 예외 객체 . 이러한 객체의 저장 공간은 지정되지 않은 방식으로 할당되고 해제됩니다.
링키지
이름은 external linkage , module linkage (since C++20) , internal linkage , 또는 no linkage 를 가질 수 있습니다:
|
(since C++20) |
- 내부 연결을 가진 이름의 엔티티는 동일한 번역 단위 내에서 다른 범위에서 재선언될 수 있습니다.
- 연결이 없는 이름의 엔티티는 동일한 범위 내에서만 재선언될 수 있습니다.
다음 연결들이 인식됩니다:
링크 없음
블록 범위에서 선언된 다음 이름들 중 어느 것도 링크를 가지지 않습니다:
- 명시적으로 선언되지 않은 변수 extern ( static 한정자와 관계없이);
- local classes 와 그 멤버 함수;
- 블록 범위에서 선언된 typedef, 열거형, 열거자 등의 다른 이름들.
외부 , 모듈, (C++20 이후) 또는 내부 링크지정으로 명시되지 않은 이름들은 선언된 스코프와 관계없이 링크를 갖지 않습니다.
내부 연결
네임스페이스 범위에서 선언된 다음 이름 중 어느 것이라도 내부 링크를 가집니다:
- 변수 , 변수 템플릿 (C++14 이후) , 함수, 또는 static 으로 선언된 함수 템플릿;
- 비템플릿 (C++14 이후) 비휘발성 const 한정 타입의 변수, 단
|
(since C++17) |
|
(since C++20) |
-
- 명시적으로 extern 으로 선언되었거나,
- 이전에 선언되었으며 이전 선언이 내부 링크를 갖지 않았던 경우;
- 익명 공용체 의 데이터 멤버.
|
또한, 이름 없는 네임스페이스 또는 이름 없는 네임스페이스 내의 네임스페이스에 선언된 모든 이름들은, 명시적으로 extern 으로 선언된 경우에도 내부 링크를 가집니다. |
(C++11부터) |
외부 링크
외부 링크를 가진 변수와 함수는 또한 언어 링크 를 가지며, 이는 서로 다른 프로그래밍 언어로 작성된 번역 단위들을 연결할 수 있게 합니다.
네임스페이스 범위에서 선언된 다음 이름들 중 무명 네임스페이스에 선언되지 않은 경우 또는 선언이 명명된 모듈에 연결되고 내보내지지 않은 경우 (C++20부터) 외부 링크를 가집니다:
- 위에 나열되지 않은 변수와 함수(즉, static 으로 선언되지 않은 함수, static 으로 선언되지 않은 비-const 변수, 그리고 extern 으로 선언된 모든 변수);
- 열거형;
- 클래스 이름, 그 멤버 함수, 정적 데이터 멤버(const 여부와 관계없음), 중첩 클래스 및 열거형, 그리고 클래스 본문 내부에서 friend 선언으로 처음 소개된 함수들의 이름;
- 위에 나열되지 않은 모든 템플릿의 이름(즉, static 으로 선언되지 않은 함수 템플릿이 아닌 것).
블록 범위에서 처음 선언된 다음 이름들 중 어느 것이라도 외부 연결을 가집니다:
- extern 으로 선언된 변수 이름;
- 함수 이름.
모듈 링크age네임스페이스 범위에서 선언된 이름들은 해당 선언이 명명된 모듈에 부착되고 내보내지지 않으며 내부 링크age를 갖지 않는 경우 모듈 링크age를 가집니다. |
(C++20부터) |
|
이 섹션은 불완전합니다
이유: 동일한 번역 단위에서 다른 링크리지로 엔티티가 선언되었을 때의 동작 설명 추가 필요 (6.6절 6항), C++20(형식 오류)과 현재 초안(형식 올바름) 간의 차이점 기술 |
정적 블록 변수
블록 변수 중 정적 또는 스레드 (C++11부터) 저장 기간을 가진 변수들은 제어가 해당 선언을 처음 통과할 때 초기화됩니다(해당 초기화가 zero- 또는 constant-initialization 인 경우는 제외하며, 이러한 초기화는 블록에 처음 진입하기 전에 수행될 수 있습니다). 이후의 모든 호출에서는 선언이 건너뜁니다.
- 초기화 과정에서 예외가 발생(throw) 하는 경우, 해당 변수는 초기화된 것으로 간주되지 않으며, 다음번에 제어 흐름이 선언문을 통과할 때 다시 초기화가 시도됩니다.
- 초기화 과정에서 변수가 초기화되는 블록으로 재귀적으로 진입하는 경우, 그 동작은 정의되지 않습니다(undefined behavior).
|
(C++11부터) |
정적 저장 기간을 가진 블록 변수의 소멸자는 프로그램 종료 시 호출됩니다 , 하지만 초기화가 성공적으로 이루어진 경우에만 해당합니다.
동일한 inline function (암시적으로 inline일 수 있음)의 모든 정의에서 정적 저장 기간을 가진 변수들은, 해당 함수가 외부 연결을 가질 경우 하나의 번역 단위에서 정의된 동일한 객체를 참조합니다.
번역 단위 지역 엔티티
번역 단위 지역 엔티티 개념은 C++20에서 표준화되었으며, 자세한 내용은 이 페이지 를 참조하십시오.
엔터티는 다음과 같은 경우 translation-unit-local (또는 줄여서 TU-local )입니다
- 내부 연결을 가진 이름을 가지고 있거나,
- 연결을 가진 이름을 가지고 있지 않으며 TU-local entity의 정의 내에서 도입되었거나,
- 템플릿 인자 또는 템플릿 선언이 TU-local entity를 사용하는 템플릿 또는 템플릿 특수화인 경우.
나쁜 일들(일반적으로 ODR 위반)은 TU-로컬이 아닌 엔티티의 타입이 TU-로컬 엔티티에 의존할 때, 또는 TU-로컬이 아닌 엔티티의 선언이 , 또는 deduction guide 가, (since C++17) 해당 범위 밖에서 TU-로컬 엔티티를 참조할 때 발생할 수 있습니다.
- 인라인이 아닌 함수 또는 함수 템플릿의 function-body
- 변수 또는 변수 템플릿의 initializer
- 클래스 정의 내의 friend declarations
- 변수의 값 사용 (해당 변수가 상수 표현식에서 사용 가능한 경우
|
이러한 사용은 모듈 인터페이스 단위 (해당 private-module-fragment 외부, 존재하는 경우) 또는 모듈 파티션에서는 허용되지 않으며, 다른 모든 컨텍스트에서는 사용이 권장되지 않습니다. 하나의 번역 단위에 나타나는 선언은 헤더 단위가 아닌 다른 번역 단위에서 선언된 TU-local 엔티티를 명시할 수 없습니다. 템플릿 을 위해 인스턴스화된 선언은 특수화의 인스턴스화 지점에 나타납니다. |
(C++20부터) |
참고 사항
최상위 네임스페이스 스코프(C에서는 파일 스코프)에서 const 이면서 extern 이 아닌 이름들은 C에서는 외부 링크를 가지지만, C++에서는 내부 링크를 가집니다.
C++11부터, auto 는 더 이상 저장 클래스 지정자가 아닌; 타입 추론을 나타내는 데 사용됩니다.
|
C에서는 register 변수의 주소를 취할 수 없지만, C++에서는 register 로 선언된 변수는 저장 클래스 지정자가 없는 변수와 의미상 구별할 수 없습니다. |
(until C++17) |
|
C++에서는 C와 달리 변수를 register 로 선언할 수 없습니다. |
(since C++17) |
서로 다른 범위에서 참조되는 내부 또는 외부 링크를 가진 thread_local 변수의 이름들은, 코드가 동일한 스레드에서 실행 중인지 다른 스레드에서 실행 중인지에 따라 동일한 인스턴스나 서로 다른 인스턴스를 참조할 수 있습니다.
extern 키워드는 언어 링크 와 명시적 템플릿 인스턴스화 선언 을 지정하는 데에도 사용될 수 있습니다. 그러나 이러한 경우에는 저장 클래스 지정자가 아닙니다 (단, 선언이 언어 링크 명세 내에 직접 포함된 경우는 예외이며, 이때 선언은 extern 지정자를 포함하는 것처럼 처리됩니다).
저장 클래스 지정자는 thread_local 를 제외하고 명시적 특수화(explicit specializations) 와 명시적 인스턴스화(explicit instantiations) 에서 허용되지 않습니다:
template<class T> struct S { thread_local static int tlm; }; template<> thread_local int S<float>::tlm = 0; // 여기에는 "static"이 나타나지 않음
|
const (또는 constexpr 에 의해 암시될 수 있음) 변수 템플릿은 기본적으로 내부 링크를 가졌으며, 이는 다른 템플릿 엔티티와 일관되지 않았습니다. 결함 보고서 CWG2387 이 이를 수정했습니다. |
(C++14 이후) |
inline
는 기본적으로 외부 링크를 부여하여
CWG2387
에 대한 해결책으로 작동합니다. 이것이
inline
이 많은 변수 템플릿에
추가되었다가
CWG2387이 수락된 후
제거된
이유입니다. 지원되는 컴파일러가 CWG2387을 구현하지 않은 동안 표준 라이브러리 구현체들도
inline
을 사용해야 합니다. 자세한 내용은
GCC Bugzilla #109126
과
MSVC STL PR #4546
을 참조하십시오.
|
(C++17 이후) |
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_threadsafe_static_init
|
200806L
|
(C++11) | 동시성을 고려한 동적 초기화 및 소멸 |
키워드
auto , register , static , extern , thread_local , mutable
예제
#include <iostream> #include <mutex> #include <string> #include <thread> thread_local unsigned int rage = 1; std::mutex cout_mutex; void increase_rage(const std::string& thread_name) { ++rage; // modifying outside a lock is okay; this is a thread-local variable std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for " << thread_name << ": " << rage << '\n'; } int main() { std::thread a(increase_rage, "a"), b(increase_rage, "b"); { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for main: " << rage << '\n'; } a.join(); b.join(); }
가능한 출력:
Rage counter for a: 2 Rage counter for main: 1 Rage counter for b: 2
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 216 | C++98 |
클래스 범위의 무명 클래스 및 열거형이 네임스페이스 범위의 것들과
다른 링크를 가짐 |
이 모든 범위에서 외부 링크를
가짐 |
| CWG 389 | C++98 |
링크가 없는 이름은 링크가 있는
개체를 선언하는 데 사용되어서는 안 됨 |
링크가 없는 타입은 링크가 있는 변수나 함수의
타입으로 사용되어서는 안 되며, 해당 변수나 함수가 C 언어 링크를 가지는 경우는 예외 |
| CWG 426 | C++98 |
개체가 동일한 번역 단위에서 내부 링크와
외부 링크 모두로 선언될 수 있음 |
이 경우 프로그램은 형식 오류 |
| CWG 527 | C++98 |
CWG 389의 해결책으로 도입된 타입 제한이
자체 번역 단위 외부에서 이름을 지정할 수 없는 변수와 함수에도 적용됨 |
이러한 변수와 함수(즉, 링크가 없거나
내부 링크를 가지거나 무명 네임스페이스 내에서 선언된 경우)에 대해 제한이 해제됨 |
| CWG 809 | C++98 | register 가 거의 기능을 하지 않음 | 사용 중단됨 |
| CWG 1648 | C++11 |
static
이
thread_local 과 extern 이 결합된 경우에도 암시됨 |
다른 저장 클래스 지정자가
없는 경우에만 암시됨 |
| CWG 1686 |
C++98
C++11 |
네임스페이스 범위에서 선언된 비정적 변수의 이름은
명시적으로 const (C++98) 또는 constexpr (C++11)로 선언된 경우에만 내부 링크를 가짐 |
타입이 const 한정된
경우만 요구 |
| CWG 2019 | C++98 |
참조 멤버의 저장 기간이
명시되지 않음 |
해당 완전 개체와 동일 |
| CWG 2387 | C++14 |
const 한정된 변수 템플릿이 기본적으로
내부 링크를 가지는지 불명확 |
const 한정자는 변수 템플릿이나
해당 인스턴스의 링크에 영향을 주지 않음 |
| CWG 2533 | C++98 |
암시적으로 생성된 개체의
저장 기간이 불명확 |
명확해짐 |
| CWG 2850 | C++98 |
함수 매개변수의 저장 공간이
언제 해제되는지 불명확 |
명확해짐 |
| CWG 2872 | C++98 | "참조될 수 있음"의 의미가 불명확 | 문구 개선 |
| P2788R0 | C++20 |
네임스페이스에서 const 한정된 변수를 선언하면
모듈 단위에서도 내부 링크를 부여함 |
내부 링크가 부여되지 않음 |
참조문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 6.7.5 저장 기간 [basic.stc]
- C++20 표준(ISO/IEC 14882:2020):
-
- 6.7.5 저장 기간 [basic.stc]
- C++17 표준(ISO/IEC 14882:2017):
-
- 6.7 저장 기간 [basic.stc]
- C++14 표준(ISO/IEC 14882:2014):
-
- 3.7 저장 기간 [basic.stc]
- C++11 표준(ISO/IEC 14882:2011):
-
- 3.7 저장 기간 [basic.stc]
- C++03 표준(ISO/IEC 14882:2003):
-
- 3.7 저장 기간 [basic.stc]
- C++98 표준(ISO/IEC 14882:1998):
-
- 3.7 저장 기간 [basic.stc]
참고 항목
|
C documentation
for
storage duration
|