Namespaces
Variants

Storage class specifiers

From 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