Пространства имён
Варианты
Действия

Локальные сущности единицы трансляции (начиная с C++20)

Материал из cppreference.com
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Обьявления
Объявления
ссылка
указатель
массив
Block declarations
простое объявление
объявление структурных привязок (C++17)
объявление псевдонимов(C++11)
объявление псевдонимов пространств имён
using-declaration
директива using
объявление static_assert (C++11)
определение asm
объявление непрозрачного enum(C++11)
Другие объявления
определение пространств имён
объявление функции
объявление шаблона класса
объявление шаблона функции
явное инстанцирование шаблона(C++11)
явная специализация шаблона
спецификация связывания
объявление атрибута (C++11)
пустое объявление
 

Локальные сущности единицы трансляции (TU-local - Translation unit local) вводятся для предотвращения экспорта и использования сущностей, которые должны быть локальными (не используемыми ни в одной другой единице трансляции), в других единицах трансляции.

Пример из Понимание Модулей C++: Часть 2 иллюстрирует проблему отсутствия ограничения экспорта:

// Модульная единица без ограничений TU-local
export module Foo;

import <iostream>;

namespace {
   class LolWatchThis {        // внутреннее связывание, не может быть экспортирована
       static void say_hello() {
           std::cout << "Всем привет\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-локальным, если он

  1. тип, функция, переменная или шаблон, который
    1. имеет имя с внутренним связыванием, или
    2. не имеет имени со связыванием и объявляется или вводится с помощью лямбда-выражения, в определении TU-локальной сущности,
  2. тип без имени, который определён вне спецификатора класса, тела функции или инициализатора или введён спецификатором определённого типа (спецификатором типа, спецификатором класса или спецификатором перечисления), который используется для объявления только TU-локальных сущностей,
  3. специализация TU-локального шаблона,
  4. специализация шаблона с любым TU-локальным аргументом шаблона или
  5. специализация шаблона, чьё объявление (возможно, инстанцированное) является экспортированием (определено ниже).
// 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; }
};

Значение или объект является TU-локальным, если

  1. это указатель на TU-локальную функцию или объект, связанный с TU-локальной переменной, или
  2. это объект типа класса или массива, и любой из его подобъектов или любой объект или функция, на которые ссылаются его нестатические элементы данных ссылочного типа, является 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-локальный: массив TU-локальных constexpr объектов
struct tul_class { int mem; };
tul_class tul_obj{tul_const};       // TU-локальный: имеет constexpr TU-локальный объект
                                    // элемент

Экспозиция

Объявление D именует сущность E, если

  1. D содержит лямбда-выражение с типом замыкания E,
  2. E не является функцией или шаблоном функции, а D содержит выражение-идентификатор, спецификатор-типа, спецификатор-вложенного-имени, имя-шаблона или имя-концепта, обозначающие E, или
  3. E это функция или шаблон функции, а D содержит выражение, которое именует E или выражение-идентификатор, которое ссылается на набор перегрузок, содержащих E.
// лямбда именование
auto x = [] {}; // именует decltype(x)

// нефункциональное (шаблонное) именование
int y1 = 1;                      // именует y1 (выражение-идентификатор)
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 << "без перегрузки"; return 0; }
int z2(int arg)      { std::cout << "перегрузка 1";  return 1; }
int z2(double arg)   { std::cout << "перегрузка 2";  return 2; }

int val1 = z1(0); // именует z1
int val2 = z2(0); // именует z2 ( int z2(int) )

Объявление является экспозицией, если оно именует TU-локальный объект, игнорируя

  1. тело функции для невстраиваемой функции или шаблона функции (но не выведенный тип возвращаемого значения для (возможно, инстанцированного) определения функции с объявленным типом возвращаемого значения, использующим тип заполнитель),
  2. инициализатор для переменной или шаблона переменной (но не тип переменной),
  3. объявления друзей в определении класса и
  4. любая ссылка на не-volatile константный объект или ссылка с внутренним связыванием или без него, инициализированная константным выражением, которое не используется odr,

или определяет переменную constexpr, инициализированную TU-локальным значением.

TU-локальные ограничения

Если (возможно, инстанцированное) объявление или руководство по выводу для не TU-локальной сущности в единице интерфейса модуля (за пределами частного-фрагмента-модуля, если таковой имеется) или в разделе модуля представляет собой экспозицию, программа некорректна. Такое объявление в любом другом контексте не рекомендуется.

Если объявление, которое появляется в одной единице трансляции, именует 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); } // Хорошо, но специализация может быть экспозицией

Единица трансляции #2:

module A;
void other() {
    g<0>();                  // OK: специализация инстанцирована явно
    g<1>();                  // ошибка: инстанцирование использует TU-local
    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 нельзя использовать
                             // в константных выражениях
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.