Translation-unit-local entities (since C++20)
번역-단위-지역(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) 입니다
-
이름이
internal linkage
를 가지는, 또는 링크를 갖지 않는 이름을 가지고 TU-로컬 엔티티 정의 내에서 선언되거나
lambda expression
에 의해 도입된 타입, 함수, 변수 또는 템플릿
- 이름이 internal linkage 를 가지는, 또는
- 링크를 갖지 않는 이름을 가지고 TU-로컬 엔티티 정의 내에서 선언되거나 lambda expression 에 의해 도입된
- class-specifier , 함수 본문, 또는 초기화자 외부에서 정의된 이름 없는 타입이거나, 오직 TU-로컬 엔티티만 선언하는 데 사용되는 정의 타입 지정자(타입 지정자, 클래스 지정자 또는 열거형 지정자)에 의해 도입된 타입,
- TU-로컬 템플릿의 특수화,
- TU-로컬 템플릿 인자를 가지는 템플릿의 특수화, 또는
- 선언(인스턴스화된 것일 수 있음)이 노출(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; } };
|
이 섹션은 불완전합니다
이유: 규칙 #1.2, #2 및 #5의 예시가 누락됨 |
값이나 객체는 다음 중 하나에 해당하는 경우 TU-local 입니다
- 그것이 TU-지역 함수이거나 TU-지역 함수에 대한 포인터이거나, TU-지역 변수와 연관된 객체인 경우, 또는
- 그것이 클래스 또는 배열 타입의 객체이고 해당 객체의 하위 객체 중 하나 또는 해당 객체의 비정적 참조 타입 데이터 멤버가 참조하는 객체나 함수 중 하나가 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는 다음과 같은 경우
- D가 클로저 타입이 E인 람다 표현식을 포함하는 경우,
- E가 함수나 함수 템플릿이 아니고 D가 E를 나타내는 id-표현식, 타입 지정자, 중첩 이름 지정자, 템플릿 이름, 또는 개념 이름을 포함하는 경우, 또는
- 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) 입니다.
- 인라인이 아닌 함수 또는 함수 템플릿의 함수 본문(단, 선언된 반환 타입이 placeholder type 을 사용하는 함수의 (인스턴스화된) 정의에 대한 추론된 반환 타입은 제외),
- 변수 또는 변수 템플릿의 초기화식(단, 변수의 타입은 제외),
- 클래스 정의 내의 friend 선언, 그리고
- 상수 표현식으로 초기화된 내부 연결 또는 무연결 비휘발성 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은 여기서 상수 표현식에서 사용할 수 없음 }
|
이 섹션은 불완전합니다
이유: 예제들이 너무 복잡하고, 더 나은 배열이 필요함 |