Spec-Zone.ru › C++

Сущности локальные для трансляционной единицы (с 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
}

ТУ-локальные сущности

Сущность является ТУ-локальной, если она

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

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

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

Spec-Zone.ru

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