Сущности локальные для трансляционной единицы (с C++20)
Сущности локальные для трансляционной единицы (ТУ-локальные) введены, чтобы предотвратить случай, когда сущности, которые должны быть локальными (не используются ни в какой другой трансляционной единице), могут быть экспонированы и использованы в других трансляционных единицах.
Пример из Понимание модулей C++: Часть 2 иллюстрирует проблему не ограниченного экспонирования:
// 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
}ТУ-локальные сущности
Сущность является ТУ-локальной, если она
- тип, функция, переменная или шаблон,
- имеющие имя с внутренней связью, или
- не имеющие имени с связью и объявленные, или введенные выражением лямбда-выражением, внутри определения ТУ-локальной сущности,
- тип без имени, определённый вне спецификатора класса, тела функции или инициализатора, или введённый спецификатором типа (спецификатор типа, спецификатор класса или спецификатор перечисления), который используется для объявления только ТУ-локальных сущностей,
- специализация ТУ-локального шаблона,
- специализация шаблона с любым ТУ-локальным аргументом шаблона, или
- специализация шаблона, чьё (возможно, инстанцированное) объявление является экспозицией (определено ниже).
// 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; }
};Значение или объект является ТУ-локальным, если либо
- это, или указатель на, ТУ-локальную функцию или объект, связанный с ТУ-локальной переменной, или
- это объект типа класса или массива, и любой из его подобъектов или любой из объектов или функций, которым ссылаются его нестатические данные-члены ссылочного типа, являются ТУ-локальными и используемы в константных выражениях.
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Экспозиции
Объявление D называет сущность E, если
- D содержит лямбда-выражение, тип замыкания которого является E,
- E не является функцией или шаблоном функции и D содержит id-выражение, спецификатор типа, квалификатор имени, имя шаблона или имя концепта, обозначающее E, или
- E является функцией или шаблоном функции и D содержит выражение, называющее E, или id-выражение, которое ссылается на набор перегрузок, содержащий E.
// 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) )Объявление является экспозицией, если оно либо называет ТУ-локальную сущность, игнорируя
- тело функции для нелинейной функции или шаблона функции (но не выведенный тип возврата для (возможно, инстанцированного) определения функции с объявленным типом возврата, использующего тип-заполнитель),
- инициализатор для переменной или шаблона переменной (но не тип переменной),
- объявления друзей в определении класса, и
- любую ссылку на непеременную константу-объект или ссылку с внутренней или без связи, инициализированную константным выражением, которое не является odr-использованием,
или определяет константную переменную, инициализированную ТУ-локальным значением.
Ограничения ТУ-локальных сущностей
Если (возможно, инстанцированное) объявление или руководство по выводу для не-ТУ-локальной сущности в модуле интерфейсной единице (вне фрагмента модуля private, если таковой имеется) или модульном разделе является экспозицией, программа неверна. Такое объявление в любом другом контексте устарело.
Если объявление, которое появляется в одной трансляционной единице, называет ТУ-локальную сущность, объявленную в другой трансляционной единице, которая не является единицей заголовка, программа неверна. Объявление, инстанцированное для специализации шаблона, появляется в момент инстанциации специализации.
Пример
Трансляционная единица #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
}
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/tu_local