TU-로컬 엔티티
TU-로컬 엔티티 (Translation-unit-local entities)
모듈(module)은 어떤 엔티티가 다른 번역 단위에서 쓰여서는 안 되는데, 그런 게 실수로 노출되어 밖에서 쓰여 버리는 문제를 막기 위한 장치가 필요해요. TU-로컬(TU-local) 엔티티는 바로 그 개념이에요 — "이 엔티티는 이 번역 단위 안에서만 쓰여야 한다"는 걸 컴파일러가 검사하게 해 주죠. 특히 C++ 모듈을 다룰 때 중요한 규칙이에요.
출처: cppreference
본문
TU-로컬 엔티티
다음 중 하나에 해당하면 그 엔티티는 TU-로컬이에요.
- 다음을 만족하는 타입·함수·변수·템플릿
- 내부 링키지(internal linkage)를 가진 이름을 가지거나,
- 링키지를 가진 이름이 없으면서 TU-로컬 엔티티의 정의 안에서 선언되거나 람다 표현식으로 도입된 것
- 클래스 지정자, 함수 본문, 또는 초기화기 밖에서 정의된 이름 없는 타입이나, TU-로컬 엔티티만 선언하는 데 쓰이는 defining-type-specifier(type-specifier, class-specifier, enum-specifier)로 도입된 이름 없는 타입
- TU-로컬 템플릿의 특수화
- TU-로컬 템플릿 인자를 가진 템플릿의 특수화
- (인스턴스화됐을 수도 있는) 선언이 아래에서 설명할 노출(exposure) 인 템플릿의 특수화
// TU-local entities with internal linkage
namespace { // all names declared in unnamed namespace have internal linkage
int tul_var = 1; // TU-local variable
int tul_func() { return 1; } // TU-local function
struct tul_type { int mem; }; // TU-local (class) type
}
template<typename T>
static int tul_func_temp() { return 1; } // TU-local template
// TU-local template specialization
template<>
static int tul_func_temp<int>() { return 3; } // TU-local specialization
// template specialization with TU-local template argument
template <> struct std::hash<tul_type> { // TU-local specialization
std::size_t operator()(const tul_type& t) const { return 4u; }
};
| 이 절은 불완전해요 | |
|---|---|
| 이유: 규칙 #1.2, #2, #5의 예시가 빠져 있음 |
다음 중 하나에 해당하면 그 값이나 객체가 TU-로컬이에요.
- TU-로컬 함수 또는 TU-로컬 변수에 연관된 객체(또는 그에 대한 포인터)인 경우
- 클래스·배열 타입의 객체이며, 그 하위 객체(subobject) 중 하나나 참조 타입 비정적 데이터 멤버가 가리키는 객체·함수 중 하나가 TU-로컬이면서 상수 표현식에서 쓸 수 있는 경우
static int tul_var = 1; // TU-local variable
static int tul_func() { return 1; } // TU-local function
int* tul_var_ptr = &tul_var; // TU-local: pointer to TU-local variable
int (* tul_func_ptr)() = &tul_func; // TU-local: pointer to TU-local function
constexpr static int tul_const = 1; // TU-local variable usable in constant expressions
int tul_arr[] = { tul_const }; // TU-local: array of constexpr TU-local object
struct tul_class { int mem; };
tul_class tul_obj{tul_const}; // TU-local: has member constexpr TU-local object
노출 (Exposures)
선언 D가 어떤 엔티티 E를 가리킨다는 것은 다음 중 하나를 뜻해요.
- D가 클로저 타입이 E인 람다 표현식을 포함하는 경우
- E가 함수나 함수 템플릿이 아니고, D가 E를 나타내는 id-표현식, type-specifier, nested-name-specifier, template-name, concept-name을 포함하는 경우
- E가 함수나 함수 템플릿이고, D가 E를 가리키는 표현식이나 E를 포함한 오버로드 집합을 가리키는 id-표현식을 포함하는 경우
// lambda naming
auto x = [] {}; // names decltype(x)
// non-function (template) naming
int y1 = 1; // names y1 (id-expression)
struct y2 { int mem; };
y2 y2_obj{1}; // names y2 (type-specifier)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // names y3 (nested-name-specifier)
template<typename T> int y4 = 1;
int var = y4<y2>; // names y4 (template-name)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // names y5 (concept-name)
// function (template) naming
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); // names z1
int val2 = z2(0); // names z2 ( int z2(int) )
어떤 선언은 TU-로컬 엔티티를 가리키면서 다음 항목들을 무시하면 노출이에요.
- inline이 아닌 함수나 함수 템플릿의 함수 본문(단, 자리 표시자 타입으로 선언된 반환 타입을 쓰는 함수 정의의 (인스턴스화됐을 수도 있는) 추론된 반환 타입은 제외)
- 변수나 변수 템플릿의 초기화기(변수의 타입은 제외)
- 클래스 정의 안의 friend 선언
- odr-use가 아닌, 상수 표현식으로 초기화된 내부 링키지 또는 링키지가 없는 비휘발성 const 객체·참조에 대한 모든 참조
또는 TU-로컬 값으로 초기화된 constexpr 변수를 정의하는 선언도 노출이에요.
| 이 절은 불완전해요 | |
|---|---|
| 이유: 노출에 대한 예시가 빠져 있음 |
TU-로컬 제약 (TU-local constraints)
모듈 인터페이스 단위(있는 경우 private-module-fragment 밖)나 모듈 파티션에 있는, TU-로컬이 아닌 엔티티의 (인스턴스화됐을 수도 있는) 선언 또는 그에 대한 추론 가이드(deduction guide)가 노출이면 프로그램은 ill-formed예요. 다른 문맥에서의 그런 선언은 deprecate돼요.
한 번역 단위에 나타난 선언이, 헤더 단위가 아닌 다른 번역 단위에 선언된 TU-로컬 엔티티를 가리키면 프로그램은 ill-formed예요. 템플릿 특수화를 위해 인스턴스화된 선언은 그 특수화의 인스턴스화 지점에 나타난 것으로 간주돼요.
| 이 절은 불완전해요 | |
|---|---|
| 이유: 제약에 대한 예시가 빠져 있음 |
예제
번역 단위 #1:
export module A;
static void f() {}
inline void it() { f(); } // error: is an exposure of f
static inline void its() { f(); } // OK
template<int> void g() { its(); } // OK
template void g<0>();
decltype(f) *fp; // error: f (though not its type) is TU-local
auto &fr = f; // OK
constexpr auto &fr2 = fr; // error: is an exposure of f
constexpr static auto fp2 = fr; // OK
struct S { void (&ref)(); } s{f}; // OK: value is TU-local
constexpr extern struct W { S &s; } wrap{s}; // OK: value is not TU-local
static auto x = []{ f(); }; // OK
auto x2 = x; // error: the closure type is TU-local
int y = ([]{ f(); }(), 0); // error: the closure type is not TU-local
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, but a specialization might be an exposure
번역 단위 #2:
module A;
void other()
{
g<0>(); // OK: specialization is explicitly instantiated
g<1>(); // error: instantiation uses TU-local its
h(N::A{}); // error: overload set contains TU-local N::adl(int)
h(0); // OK: calls adl(double)
adl(N::A{}); // OK; N::adl(int) not found, calls N::adl(N::A)
fr(); // OK: calls f
constexpr auto ptr = fr; // error: fr is not usable in constant expressions here
}
| 이 절은 불완전해요 | |
|---|---|
| 이유: 예시가 너무 복잡해서 더 나은 정리가 필요함 |
더 알아보기
- 내부 링키지와 저장 기간, 링키지의 종류는 링키지 문서에서 더 자세히 다뤄요.
- 모듈 인터페이스 단위와 모듈 파티션, export의 규칙은 모듈 문서에서 확인할 수 있어요.
- cppreference의 TU-로컬 엔티티 원문에서 결함 보고(defect report) 기록을 더 볼 수 있어요.