Namespaces
Variants

Translation-unit-local entities (since C++20)

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

번역-단위-지역(TU-local) 엔티티는 지역적이어야 하는(다른 번역 단위에서 사용되지 않는) 엔티티가 다른 번역 단위에서 노출되고 사용되는 것을 방지하기 위해 도입되었습니다.

Understanding C++ Modules: Part 2 에서 가져온 예시는 노출 제한을 적용하지 않을 때 발생하는 문제를 보여줍니다:

// TU-로컬 제약 조건이 없는 모듈 유닛
export module Foo;
import <iostream>;
namespace
{
   class LolWatchThis {        // 내부 링크, export 불가
       static void say_hello()
       {
           std::cout << "Hello, everyone!\n";
       }
   };
}
export LolWatchThis lolwut() { // LolWatchThis가 반환 타입으로 노출됨
    return LolWatchThis();
}
// main.cpp
import Foo;
int main()
{
    auto evil = lolwut();        // 'evil'은 'LolWatchThis' 타입을 가짐
    decltype(evil)::say_hello(); // 'LolWatchThis'의 정의가 더 이상 내부적이지 않음
}

목차

TU-로컬 엔티티

엔터티는 다음과 같은 경우 TU-로컬(TU-local) 입니다

  1. 이름이 internal linkage 를 가지는, 또는 링크를 갖지 않는 이름을 가지고 TU-로컬 엔티티 정의 내에서 선언되거나 lambda expression 에 의해 도입된 타입, 함수, 변수 또는 템플릿
    1. 이름이 internal linkage 를 가지는, 또는
    2. 링크를 갖지 않는 이름을 가지고 TU-로컬 엔티티 정의 내에서 선언되거나 lambda expression 에 의해 도입된
  2. class-specifier , 함수 본문, 또는 초기화자 외부에서 정의된 이름 없는 타입이거나, 오직 TU-로컬 엔티티만 선언하는 데 사용되는 정의 타입 지정자(타입 지정자, 클래스 지정자 또는 열거형 지정자)에 의해 도입된 타입,
  3. TU-로컬 템플릿의 특수화,
  4. TU-로컬 템플릿 인자를 가지는 템플릿의 특수화, 또는
  5. 선언(인스턴스화된 것일 수 있음)이 노출(exposure)인 템플릿의 특수화(아래에 정의됨).
// 내부 링크지를 갖는 TU-로컬 엔티티들
namespace { // 이름 없는 네임스페이스 내에 선언된 모든 이름들은 내부 링크지를 가짐
    int tul_var = 1;                          // TU-로컬 변수
    int tul_func() { return 1; }              // TU-로컬 함수
    struct tul_type { int mem; };             // TU-로컬 (클래스) 타입
}
template<typename T>
static int tul_func_temp() { return 1; }      // TU-로컬 템플릿
// TU-로컬 템플릿 특수화
template<>
static int tul_func_temp<int>() { return 3; } // TU-로컬 특수화
// TU-로컬 템플릿 인자를 사용하는 템플릿 특수화
template <> struct std::hash<tul_type> {      // TU-로컬 특수화
    std::size_t operator()(const tul_type& t) const { return 4u; }
};

값이나 객체는 다음 중 하나에 해당하는 경우 TU-local 입니다

  1. 그것이 TU-지역 함수이거나 TU-지역 함수에 대한 포인터이거나, TU-지역 변수와 연관된 객체인 경우, 또는
  2. 그것이 클래스 또는 배열 타입의 객체이고 해당 객체의 하위 객체 중 하나 또는 해당 객체의 비정적 참조 타입 데이터 멤버가 참조하는 객체나 함수 중 하나가 TU-지역이며 상수 표현식에서 사용 가능한 경우.
static int tul_var = 1;             // TU-지역 변수
static int tul_func() { return 1; } // TU-지역 함수
int* tul_var_ptr = &tul_var;        // TU-지역: TU-지역 변수에 대한 포인터
int (* tul_func_ptr)() = &tul_func; // TU-지역: TU-지역 함수에 대한 포인터
constexpr static int tul_const = 1; // 상수 표현식에서 사용 가능한 TU-지역 변수
int tul_arr[] = { tul_const };      // TU-지역: constexpr TU-지역 객체의 배열
struct tul_class { int mem; };
tul_class tul_obj{tul_const};       // TU-지역: constexpr TU-지역 객체를 멤버로 가짐

노출

선언 D가 이름짓는 엔티티 E는 다음과 같은 경우

  1. D가 클로저 타입이 E인 람다 표현식을 포함하는 경우,
  2. E가 함수나 함수 템플릿이 아니고 D가 E를 나타내는 id-표현식, 타입 지정자, 중첩 이름 지정자, 템플릿 이름, 또는 개념 이름을 포함하는 경우, 또는
  3. E가 함수나 함수 템플릿이고 D가 E를 명명하는 표현식 또는 E를 포함하는 오버로드 집합을 참조하는 id-표현식을 포함하는 경우.
// 람다 명명
auto x = [] {}; // decltype(x)를 명명함
// 비함수 (템플릿) 명명
int y1 = 1;                      // y1을 명명함 (id-expression)
struct y2 { int mem; };
y2 y2_obj{1};                    // y2를 명명함 (type-specifier)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // y3을 명명함 (nested-name-specifier)
template<typename T> int y4 = 1;
int var = y4<y2>;                // y4를 명명함 (template-name)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // y5를 명명함 (concept-name)
// 함수 (템플릿) 명명
int z1(int arg)    { std::cout << "no overload"; return 0; }
int z2(int arg)    { std::cout << "overload 1";  return 1; }
int z2(double arg) { std::cout << "overload 2";  return 2; }
int val1 = z1(0); // z1을 명명함
int val2 = z2(0); // z2를 명명함 ( int z2(int) )

선언은 TU-지역 엔티티를 이름으로 지정하거나, 무시하는 경우 노출(exposure) 입니다.

  1. 인라인이 아닌 함수 또는 함수 템플릿의 함수 본문(단, 선언된 반환 타입이 placeholder type 을 사용하는 함수의 (인스턴스화된) 정의에 대한 추론된 반환 타입은 제외),
  2. 변수 또는 변수 템플릿의 초기화식(단, 변수의 타입은 제외),
  3. 클래스 정의 내의 friend 선언, 그리고
  4. 상수 표현식으로 초기화된 내부 연결 또는 무연결 비휘발성 const 객체나 참조에 대한 모든 참조 중 odr-use 가 아닌 경우,

또는 TU-지역 값으로 초기화된 constexpr 변수를 정의합니다.

TU-로컬 제약 조건

만약 (인스턴스화되었을 수 있는) 선언 이나 비-TU-지역 엔티티에 대한 deduction guide 모듈 인터페이스 단위 (private-module-fragment 외부, 존재하는 경우)나 모듈 파티션에서 노출(exposure)이라면, 프로그램은 ill-formed입니다. 다른 어떤 맥락에서의 이러한 선언은 deprecated됩니다.

한 번역 단위에 나타나는 선언이 헤더 단위가 아닌 다른 번역 단위에서 선언된 TU-지역 엔티티를 지칭하는 경우, 프로그램은 형식에 맞지 않습니다. 템플릿 특수화를 위해 인스턴스화된 선언은 해당 특수화의 인스턴스화 지점에 나타납니다.

예제

번역 단위 #1:

export module A;
static void f() {}
inline void it() { f(); }         // 오류: f의 노출입니다
static inline void its() { f(); } // OK
template<int> void g() { its(); } // OK
template void g<0>();
decltype(f) *fp;                             // 오류: f(그 타입은 아니지만)는 TU-로컬입니다
auto &fr = f;                                // OK
constexpr auto &fr2 = fr;                    // 오류: f의 노출입니다
constexpr static auto fp2 = fr;              // OK
struct S { void (&ref)(); } s{f};            // OK: 값은 TU-로컬입니다
constexpr extern struct W { S &s; } wrap{s}; // OK: 값은 TU-로컬이 아닙니다
static auto x = []{ f(); }; // OK
auto x2 = x;                // 오류: 클로저 타입이 TU-로컬입니다
int y = ([]{ f(); }(), 0);  // 오류: 클로저 타입이 TU-로컬이 아닙니다
int y2 = (x, 0);            // OK
namespace N
{
    struct A {};
    void adl(A);
    static void adl(int);
}
void adl(double);
inline void h(auto x) { adl(x); } // OK, 하지만 특수화는 노출이 될 수 있습니다

번역 단위 #2:

module A;
void other()
{
    g<0>();                  // OK: 명시적으로 인스턴스화된 특수화
    g<1>();                  // 오류: 인스턴스화가 TU-로컬 its를 사용함
    h(N::A{});               // 오류: 오버로드 집합에 TU-로컬 N::adl(int)가 포함됨
    h(0);                    // OK: adl(double) 호출
    adl(N::A{});             // OK; N::adl(int)를 찾을 수 없음, N::adl(N::A) 호출
    fr();                    // OK: f 호출
    constexpr auto ptr = fr; // 오류: fr은 여기서 상수 표현식에서 사용할 수 없음
}