Namespaces
Variants

Storage class specifiers

From ko.cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

저장 클래스 지정자는 이름의 decl-specifier-seq 의 일부입니다. 선언 구문 . 이름의 범위 와 함께, 이들은 이름의 두 가지 독립적인 속성인 저장 기간 과 링크 을 제어합니다.

목차

저장 기간

저장 기간 은 객체를 포함하는 저장 공간의 최소 잠재적 수명을 정의하는 객체 의 속성입니다. 저장 기간은 객체를 생성하는 데 사용된 구성에 의해 결정되며 다음 중 하나입니다:

  • static 저장 기간
  • 스레드 저장 기간 (thread storage duration, 스레드 지역 저장 기간으로도 알려짐)
(C++11부터)
  • 자동 저장 기간
  • 동적 저장 기간

정적 , 스레드, (C++11부터) 그리고 자동 저장 기간은 선언 에 의해 도입된 객체들과 임시 객체들 과 연관됩니다. 동적 저장 기간은 new 표현식 으로 생성된 객체들이나 암묵적으로 생성된 객체들 과 연관됩니다.

저장 기간 범주는 참조에도 적용됩니다.

subobjects 와 참조 멤버들의 저장 기간은 그들의 완전한 객체의 저장 기간과 동일합니다.

지정자

다음 키워드는 storage class specifiers 입니다:

  • auto
(C++11 이전)
  • register
(C++17 이전)
  • static
  • thread_local
(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 을 가집니다:

  • 스레드 저장 기간을 가지지 않습니다.
(since C++11)

이러한 개체들의 저장 기간은 프로그램이 실행되는 동안 지속됩니다.

스레드 저장 기간

thread_local 로 선언된 모든 변수는 스레드 저장 기간 을 가집니다.

이러한 개체들의 저장 공간은 해당 개체들이 생성된 스레드의 수명 동안 지속됩니다. 각 스레드마다 별개의 객체 또는 참조가 존재하며, 선언된 이름을 사용하면 현재 스레드와 연관된 개체를 참조하게 됩니다.

(C++11부터)

자동 저장 기간

다음 변수들은 automatic storage duration 을 가집니다:

  • 블록 범위 에 속하며 명시적으로 static , thread_local , (C++11부터) 또는 extern 으로 선언되지 않은 변수들. 이러한 변수들의 저장 공간은 해당 변수가 생성된 블록이 종료될 때까지 유지됩니다.
  • 매개변수 범위(즉, 함수 매개변수)에 속하는 변수들. 함수 매개변수의 저장 공간은 해당 매개변수의 소멸 직후까지 유지됩니다.

동적 저장 기간

프로그램 실행 중 다음 방법으로 생성된 객체는 dynamic storage duration 을 가집니다:

링키지

이름은 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 한정 타입의 변수, 단
  • they are inline,
(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부터)

정적 블록 변수

블록 변수 중 정적 또는 스레드 (C++11부터) 저장 기간을 가진 변수들은 제어가 해당 선언을 처음 통과할 때 초기화됩니다(해당 초기화가 zero- 또는 constant-initialization 인 경우는 제외하며, 이러한 초기화는 블록에 처음 진입하기 전에 수행될 수 있습니다). 이후의 모든 호출에서는 선언이 건너뜁니다.

  • 초기화 과정에서 예외가 발생(throw) 하는 경우, 해당 변수는 초기화된 것으로 간주되지 않으며, 다음번에 제어 흐름이 선언문을 통과할 때 다시 초기화가 시도됩니다.
  • 초기화 과정에서 변수가 초기화되는 블록으로 재귀적으로 진입하는 경우, 그 동작은 정의되지 않습니다(undefined behavior).
  • 여러 스레드가 동일한 정적 지역 변수를 동시에 초기화하려고 시도하는 경우, 초기화는 정확히 한 번만 발생합니다(임의의 함수에 대해 std::call_once 를 사용하여 유사한 동작을 얻을 수 있습니다).
  • 이 기능의 일반적인 구현들은 이중 확인 잠금 패턴의 변형을 사용하며, 이는 이미 초기화된 지역 정적 변수에 대한 런타임 오버헤드를 단일 비원자적 부울 비교로 줄입니다.
(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