Spec-Zone.ru › C++

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

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

Пример из статьи Понимание модулей 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
}

Сущности, локальные для единицы трансляции

Сущность является локальной для единицы трансляции, если она

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

  1. D содержит лямбда-выражение, тип замыкания которого — E,
  2. E не является функцией или шаблоном функции, а D содержит выражение-идентификатор, спецификатор типа, спецификатор вложенного имени, имя шаблона или имя концепта, обозначающее E, или
  3. E является функцией или шаблоном функции, а D содержит выражение, называющее E, либо выражение-идентификатор, ссылающееся на набор перегрузок, содержащий 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) )

Объявление является экспонированием, если оно называет сущность, локальную для единицы трансляции, за исключением случаев, когда это происходит в

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

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

Ограничения для сущностей, локальных для единицы трансляции

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

Если объявление, появляющееся в одной единице трансляции, называет сущность, локальную для единицы трансляции и объявленную в другой единице трансляции, не являющейся модулем заголовка, программа некорректна. Объявление, инстанцированное для специализации шаблона, появляется в точке инстанцирования этой специализации.

Пример

Единица трансляции № 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/cpp/language/tu_local

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API