Translation-unit-local (TU-local) entities are introduced to prevent entities that are supposed to be local (not used in any other translation unit) being exposed and used in other translation units.
An example from Understanding C++ Modules: Part 2 illustrates the problem of not constraining exposures:
// Module unit without TU-local constraints export module Foo; import <iostream>; namespace { class LolWatchThis { // internal linkage, cannot be exported static void say_hello() { std::cout << "Hello, everyone!\n"; } }; } export LolWatchThis lolwut() { // LolWatchThis is exposed as return type return LolWatchThis(); }
// main.cpp import Foo; int main() { auto evil = lolwut(); // 'evil' has type of 'LolWatchThis' decltype(evil)::say_hello(); // definition of 'LolWatchThis' is not internal anymore }
An entity is TU-local if it is.
// 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; } };
A value or object is TU-local if either.
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
A declaration D names an entity E if.
// 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) )
A declaration is an exposure if it either names a TU-local entity, ignoring.
or defines a constexpr variable initialized to a TU-local value.
If a (possibly instantiated) declaration of, or a deduction guide for, a non-TU-local entity in a module interface unit (outside the private-module-fragment, if any) or module partition is an exposure, the program is ill-formed. Such a declaration in any other context is deprecated.
If a declaration that appears in one translation unit names a TU-local entity declared in another translation unit that is not a header unit, the program is ill-formed. A declaration instantiated for a template specialization appears at the point of instantiation of the specialization.
Translation unit #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
Translation unit #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 }
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/tu_local