Translation-unit-local entities (since C++20)
Перевод-единично-локальные (TU-локальные) сущности вводятся для предотвращения экспонирования и использования в других единицах трансляции сущностей, которые должны быть локальными (не использоваться в других единицах трансляции).
Пример из Understanding C++ Modules: Part 2 иллюстрирует проблему отсутствия ограничений на экспортируемые элементы:
// Модульная единица без ограничений, локальных для единицы трансляции export module Foo; import <iostream>; namespace { class LolWatchThis { // внутренняя компоновка, не может быть экспортирована 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-локальных сущностей,
- специализация TU-локального шаблона,
- специализация шаблона с любым 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-локальным если выполняется одно из условий:
- это, или является указателем на, 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 содержит id-выражение, спецификатор типа, спецификатор вложенного имени, имя шаблона или имя концепции, обозначающее E, или
- E является функцией или шаблоном функции, и D содержит выражение, которое именует E, или id-выражение, которое ссылается на набор перегрузок, содержащий E.
// именование лямбда-выражений auto x = [] {}; // именует decltype(x) // именование не-функций (шаблонов) int y1 = 1; // именует y1 (id-выражение) struct y2 { int mem; }; y2 y2_obj{1}; // именует y2 (спецификатор типа) struct y3 { int mem_func(); }; int y3::mem_func() { return 0; } // именует y3 (спецификатор вложенного имени) template<typename T> int y4 = 1; int var = y4<y2>; // именует y4 (имя шаблона) template<typename T> concept y5 = true; template<typename T> void func(T&&) requires y5<T>; // именует y5 (имя концепции) // именование функций (шаблонов) 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) )
Объявление является экспозицией , если оно либо называет сущность, локальную для единицы трансляции, игнорируя
- тело функции для не встроенной функции или шаблона функции (но не выведенный возвращаемый тип для (возможно инстанцированного) определения функции с объявленным возвращаемым типом, использующим тип-заполнитель ),
- инициализатор для переменной или шаблона переменной (но не тип переменной),
- объявления friend в определении класса, и
- любая ссылка на не-volatile const объект или ссылку с внутренней или отсутствующей линковкой, инициализированную константным выражением, которое не является odr-использованием ,
или определяет переменную constexpr, инициализированную значением, локальным для единицы трансляции.
|
Этот раздел не завершён
Причина: отсутствуют примеры для экспозиций |
Ограничения в пределах единицы трансляции
Если (возможно, инстанцированное) объявление или руководство по выводу для не-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 (хотя не его тип) является локальным для единицы трансляции auto &fr = f; // OK constexpr auto &fr2 = fr; // ошибка: является раскрытием f constexpr static auto fp2 = fr; // OK struct S { void (&ref)(); } s{f}; // OK: значение является локальным для единицы трансляции constexpr extern struct W { S &s; } wrap{s}; // OK: значение не является локальным для единицы трансляции static auto x = []{ f(); }; // OK auto x2 = x; // ошибка: тип замыкания является локальным для единицы трансляции int y = ([]{ f(); }(), 0); // ошибка: тип замыкания не является локальным для единицы трансляции 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 не может использоваться в константных выражениях здесь }
|
Этот раздел не завершён
Причина: примеры слишком сложные, требуется лучшее расположение |