4 General principles [intro]

4.1 Implementation compliance [intro.compliance]

Набор diagnosable rules состоит из всех синтаксических и семантических правил в этом международном стандарте, за исключением тех правил, которые содержат явную нотацию, что «диагностика не требуется» или которые описаны как приводящие к «неопределенному поведению».

Хотя этот международный стандарт устанавливает только требования к реализациям C ++, эти требования часто легче понять, если они сформулированы как требования к программам, частям программ или выполнению программ. Такие требования имеют следующее значение:

  • Если программа не содержит нарушений правил настоящего международного стандарта, соответствующая реализация должна в пределах своих ресурсов принять и правильно выполнить2 эту программу.

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

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

[ Note: Во время вывода и замены аргументов шаблона определенные конструкции, которые в других контекстах требуют диагностики, обрабатываются по-разному; см [temp.deduct]. ]end note

Для классов и шаблонов классов в разделах библиотеки указываются частичные определения. Private members не указаны, но каждая реализация должна предоставлять их для завершения определений в соответствии с описанием в разделах библиотеки.

Для функций, шаблонов функций, объектов и значений в разделах библиотеки указываются объявления. Реализации должны предоставлять определения, соответствующие описаниям в разделах библиотеки.

Имена, определенные в библиотеке, имеют область пространства имен ([basic.namespace]). C ++ translation unit получает доступ к этим именам через including соответствующий заголовок стандартной библиотеки.

Шаблоны, классы, функции и объекты в библиотеке имеют внешние linkage. Реализация предоставляет определения для стандартных библиотечных сущностей, если это необходимо, при объединении единиц перевода для формирования полной программы на C ++ ([lex.phases]).

Определены два типа реализаций: a hosted implementation и a freestanding implementation. Для размещенной реализации этот международный стандарт определяет набор доступных библиотек. Автономная реализация - это реализация, в которой выполнение может происходить без использования операционной системы, и имеет набор библиотек, определяемый реализацией, который включает определенные библиотеки поддержки языка ([compliance]).

Соответствующая реализация может иметь расширения (включая дополнительные библиотечные функции) при условии, что они не изменяют поведение какой-либо правильно сформированной программы. Реализации требуются для диагностики программ, использующих такие расширения, которые не соответствуют этому международному стандарту. Однако после этого они могут компилировать и выполнять такие программы.

Каждая реализация должна включать документацию, которая идентифицирует все условно поддерживаемые конструкции, которые она не поддерживает, и определяет все специфичные для локали характеристики.3

«Правильное выполнение» может включать неопределенное поведение в зависимости от обрабатываемых данных; см. пункт [intro.defs] и [intro.execution].

Эта документация также определяет поведение, определяемое реализацией; см [intro.execution].

4.2 Structure of this document [intro.structure]

Пункты [lex] до [cpp] описывают язык программирования C ++. Это описание включает подробные синтаксические спецификации в форме, описанной в [syntax]. Для удобства в Приложении [gram] повторяются все такие синтаксические спецификации.

Пункты [language.support] до [thread] и Приложение [depr] (the library clauses) описывают стандартную библиотеку C ++. Это описание включает подробные описания сущностей и макросов, составляющих библиотеку, в форме, описанной в пункте [library].

Приложение [implimits] рекомендует нижние границы возможностей соответствующих реализаций.

Приложение [diff] суммирует эволюцию C ++ с момента его первого опубликованного описания и подробно объясняет различия между C ++ и C. Некоторые функции C ++ существуют исключительно для целей совместимости; Приложение [depr] описывает эти особенности.

В этом документе каждый пример вводится «[ Example: » и заканчивается « ]». Каждое примечание начинается с символа «[ » и заканчивается знаком « ]». Примеры и примечания могут быть вложенными.end exampleNote: end note

4.3 Syntax notation [syntax]

В нотации синтаксиса, используемой в этом документе, синтаксические категории обозначаются italic типом, а буквальные слова и символы - constant width типом. Альтернативы перечислены в отдельных строках, за исключением некоторых случаев, когда длинный набор альтернатив отмечен фразой «одна из». Если альтернативный текст слишком длинный для размещения в строке, текст продолжается на последующих строках с отступом от первой. Необязательный терминальный или нетерминальный символ обозначается нижним индексом «opt», поэтому

{ expressionopt }

указывает на необязательное выражение, заключенное в фигурные скобки.

Имена для синтаксических категорий обычно выбираются в соответствии со следующими правилами:

  • X-name является использование идентификатора в контексте , который определяет его значение (например, class-name, typedef-name).

  • X-id - идентификатор без контекстно-зависимого значения (например, qualified-id).

  • X-seq - это один или несколько Xбез промежуточных разделителей (например, declaration-seqэто последовательность объявлений).

  • X-list один или несколько Xразделенных запятыми (например, identifier-listэто последовательность идентификаторов, разделенных запятыми).

4.4 The C++ memory model [intro.memory]

Фундаментальной единицей хранения в модели памяти C ++ является расширение byte. Байт, по крайней мере, достаточно велик, чтобы содержать любой член basic execution character set и восьмибитные кодовые единицы формы кодирования Unicode UTF-8, и состоит из непрерывной последовательности битов,4 количество которых определяется реализацией. Наименьший значащий бит называется low-order bit; самый старший бит называется high-order bit. Память, доступная программе C ++, состоит из одной или нескольких последовательностей смежных байтов. Каждый байт имеет уникальный адрес.

[ Note: Представление типов описано в [basic.types]. ] end note

A memory location - либо объект скалярного типа, либо максимальная последовательность смежных битовых полей, имеющих ненулевую ширину. [ Note: Различные функции языка, такие как ссылки и виртуальные функции, могут включать дополнительные области памяти, которые недоступны для программ, но управляются реализацией. ] Два или более могут обращаться к разным ячейкам памяти, не мешая друг другу. end note threads of execution

[ Note: Таким образом, битовое поле и соседнее небитовое поле находятся в разных ячейках памяти и, следовательно, могут одновременно обновляться двумя потоками выполнения без помех. То же самое относится к двум битовым полям, если одно объявлено внутри объявления вложенной структуры, а другое - нет, или если эти два разделены объявлением битового поля нулевой длины, или если они разделены небитовым -поле декларации. Одновременное обновление двух битовых полей в одной структуре небезопасно, если все поля между ними также являются битовыми полями ненулевой ширины. ] end note

[ Example: Структура, объявленная как

struct {
  char a;
  int b:5,
  c:11,
  :0,
  d:8;
  struct {int ee:8;} e;
}

содержит четыре отдельные ячейки памяти: поле a и битовых полей d и e.ee каждый представляет отдельные ячейки памяти, и могут быть изменены одновременно , не мешая друг с другом. Битовые поля b и c вместе составляют четвертую ячейку памяти. Битовые-поля b и c не могут быть одновременно изменены, но b и a, например, может быть. ] end example

Число битов в байте сообщается макросом CHAR_­BIT в заголовке <climits>.

4.5 The C++ object model [intro.object]

Конструкции в программе C ++ создают, уничтожают, обращаются к объектам, обращаются к ним и манипулируют ими. An object создается a definition, a new-expression, при неявном изменении активного члена a unionили при создании временного объекта ([conv.rval], [class.temporary]). Объект занимает область хранения в период постройки ([class.cdtor]), в течение всего lifetimeпериода и в период разрушения ([class.cdtor]). [ Note: Функция не является объектом, независимо от того, занимает ли она память так, как это делают объекты. ] Свойства объекта определяются при создании объекта. Объект может иметь расширение . У объекта есть влияющий на него объект . Объект имеет расширение . Некоторые объекты есть ; реализация генерирует информацию, связанную с каждым таким объектом, которая позволяет определить тип этого объекта во время выполнения программы. Для других объектов интерпретация найденных в них значений определяется типом (раздела ), используемого для доступа к ним. end notenamestorage duration lifetimetypepolymorphicexpressions [expr]

Объекты могут содержать другие объекты, называемые subobjects. Подобъектом может быть member subobject ([class.mem]), base class subobject (Предложение [class.derived]) или элемент массива. Объект, который не является подобъектом какого-либо другого объекта, называется complete object. Если объект создается в хранилище, связанном с подобъектом-членом или элементом массива e (который может находиться или не находиться в пределах его жизненного цикла), созданный объект является подобъектом eсодержащего его объекта, если:

  • время жизни eсодержащего объекта началось и не закончилось, и

  • хранилище для нового объекта точно перекрывает местоположение хранилища, связанное с e, и

  • новый объект того же типа, что и e (игнорируя cv-квалификацию).

[ Note: Если подобъект содержит ссылочный элемент или const подобъект, имя исходного подобъекта нельзя использовать для доступа к новому объекту ([basic.life]). ] [end noteExample:

struct X { const int n; };
union U { X x; float f; };
void tong() {
  U u = {{ 1 }};
  u.f = 5.f;                          // OK, creates new subobject of u ([class.union])
  X *p = new (&u.x) X {2};            // OK, creates new subobject of u
  assert(p->n == 2);                  // OK
  assert(*std::launder(&u.x.n) == 2); // OK
  assert(u.x.n == 2);                 // undefined behavior, u.x does not name new subobject
}

end example]

Если[expr.new]в хранилище создается полный объект ( ), связанный с другим объектом e типа «массив N unsigned char» или типа «массив N std​::​byte» ([cstddef.syn]), этот массив provides storage для созданного объекта, если:

  • время жизни e началось и не закончилось, и

  • хранилище для нового объекта полностью помещается внутри e, и

  • не существует меньшего объекта массива, удовлетворяющего этим ограничениям.

[ Note: Если эта часть массива ранее предоставляла хранилище для другого объекта, время существования этого объекта заканчивается, поскольку его хранилище было повторно использовано ([basic.life]). ] [end noteExample:

template<typename ...T>
struct AlignedUnion {
  alignas(T...) unsigned char data[max(sizeof(T)...)];
};
int f() {
  AlignedUnion<int, char> au;
  int *p = new (au.data) int;     // OK, au.data provides storage
  char *c = new (au.data) char(); // OK, ends lifetime of *p
  char *d = new (au.data + 1) char();
  return *c + *d; // OK
}

struct A { unsigned char a[32]; };
struct B { unsigned char b[16]; };
A a;
B *b = new (a.a + 8) B;      // a.a provides storage for *b
int *p = new (b->b + 4) int; // b->b provides storage for *p
                             // a.a does not provide storage for *p (directly),
                             // but *p is nested within a (see below)

end example]

Объект a - это nested within другой объект, b если:

  • a является подобъектом b, или

  • b обеспечивает хранение a, или

  • существует объект , c где a вложен в cи c вложен в b.

Для каждого объекта xсуществует объект, называемый the complete object of x, который определяется следующим образом:

  • Если x это законченный объект, то полный объект x - это сам.

  • В противном случае полный объект x является полным объектом (уникального) объекта, который содержит x.

Если полный объект, a data memberили элемент массива относятся к типу класса, его тип считается most derived class, чтобы отличить его от типа класса любого подобъекта базового класса; объект самого производного типа класса или неклассового типа называется most derived object.

Если это не a bit-field, наиболее производный объект должен иметь ненулевой размер и занимать один или несколько байтов памяти. Подобъекты базового класса могут иметь нулевой размер. Объект тривиально копируемый или standard-layout type должен занимать непрерывные байты памяти.

Если объект не является битовым полем или подобъектом базового класса нулевого размера, адрес этого объекта является адресом первого байта, который он занимает. Два объекта a и b с перекрывающимися временами жизни, которые не являются битовыми полями, могут иметь один и тот же адрес, если один вложен в другой, или если хотя бы один является подобъектом базового класса нулевого размера и они относятся к разным типам; в противном случае у них разные адреса.5

[Example:

static const char test1 = 'x';
static const char test2 = 'x';
const bool b = &test1 != &test2;      // always true

end example]

[ Note: C ++ предоставляет множество основных типов и несколько способов создания новых типов из существующих типов ([basic.types]). ]end note

По правилу «как если бы» реализации разрешено хранить два объекта по одному и тому же машинному адресу или не хранить объект вообще, если программа не может заметить разницу ([intro.execution]).

4.6 Program execution [intro.execution]

Семантические описания в этом международном стандарте определяют параметризованную недетерминированную абстрактную машину. Этот международный стандарт не предъявляет требований к структуре соответствующих реализаций. В частности, им не нужно копировать или имитировать структуру абстрактной машины. Скорее, соответствующие реализации требуются для имитации (только) наблюдаемого поведения абстрактной машины, как объяснено ниже.6

Некоторые аспекты и операции абстрактной машины описаны в этом международном стандарте как определяемые реализацией (например, sizeof(int)). Они составляют параметры абстрактной машины. Каждая реализация должна включать документацию, описывающую ее характеристики и поведение в этих отношениях.7 Такая документация должна определять экземпляр абстрактной машины, который соответствует этой реализации (далее именуется «соответствующий экземпляр»).

Некоторые другие аспекты и операции абстрактной машины описаны в этом международном стандарте как неуказанные (например, оценка выражений в a, new-initializerесли функция распределения не может выделить память ([expr.new])). Там, где это возможно, в этом международном стандарте определяется набор допустимого поведения. Они определяют недетерминированные аспекты абстрактной машины. Таким образом, экземпляр абстрактной машины может иметь более одного возможного выполнения для данной программы и данного ввода.

Некоторые другие операции описаны в этом международном стандарте как неопределенные (например, эффект попытки изменить const объект). [ Note: Этот международный стандарт не предъявляет требований к поведению программ, которые содержат неопределенное поведение. ] end note

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

Экземпляр каждого объекта automatic storage duration связан с каждой записью в его блоке. Такой объект существует и сохраняет свое последнее сохраненное значение во время выполнения блока и пока блок приостановлен (посредством вызова функции или получения сигнала).

Наименьшие требования к соответствующей реализации:

  • Доступ через изменчивые значения gl оценивается строго в соответствии с правилами абстрактной машины.

  • При завершении программы все данные, записанные в файлы, должны быть идентичны одному из возможных результатов, которые могло бы дать выполнение программы в соответствии с абстрактной семантикой.

  • Динамика ввода и вывода интерактивных устройств должна происходить таким образом, чтобы вывод подсказки фактически доставлялся до того, как программа ожидает ввода. Что представляет собой интерактивное устройство, определяется реализацией.

Все вместе они называются observable behavior программой. [ Note: Более строгое соответствие между абстрактной и фактической семантикой может быть определено каждой реализацией. ] end note

[ Note: Операторы могут быть перегруппированы в соответствии с обычными математическими правилами только в том случае, если операторы действительно ассоциативны или коммутативны.8 Например, в следующем фрагменте

int a, b;
/* ... */
a = a + 32760 + b + 5;

оператор выражения ведет себя точно так же, как

a = (((a + 32760) + b) + 5);

из-за ассоциативности и приоритета этих операторов. Таким образом, результат суммы (a + 32760) затем добавляется к b, и этот результат затем добавляется к 5, что дает значение, присвоенное a. На машине, в которой переполнение вызывает исключение и в котором диапазон значений, представленных как int есть [-32768, +32767], реализация не может переписать это выражение как

a = ((a + b) + 32765);

поскольку, если бы значения для a и b были соответственно -32754 и -15, сумма выдала a + b бы исключение, а исходное выражение - нет; также нельзя переписать это выражение как

a = ((a + 32765) + b);

или

a = (a + (b + 32765));

поскольку значения для a и b могли быть, соответственно, 4 и -8 или -17 и 12. Однако на машине, на которой переполнение не вызывает исключения и в котором результаты переполнения обратимы, приведенный выше оператор выражения может быть переписан реализацией любым из вышеперечисленных способов, потому что будет получен тот же результат. ] end note

A constituent expression определяется следующим образом:

  • Составным выражением выражения является это выражение.

  • Составные выражения a braced-init-listили a (возможно, заключенные в скобки) expression-list являются составными выражениями элементов соответствующего списка.

  • Составные выражения brace-or-equal-initializer вида являются составными выражениями . = initializer-clauseinitializer-clause

[Example:

struct A { int x; };
struct B { int y; struct A a; };
B b = { 5, { 1+1 } };

Составляющие выражения, initializer используемые для инициализации, - b это 5 и 1+1. ]end example

immediate subexpressions Выражения e являются

  • составные выражения eоперандов (пункт [expr]),

  • любой вызов функции, который e неявно вызывает,

  • если e является a lambda-expression, инициализация сущностей, захваченных копией, и составляющих выражений initializerобъекта init-captures,

  • if e является function call или неявно вызывает функцию, составляющие выражения каждого из них, default argument используемые в вызове, или

  • если e создает aggregate объект, составляющие выражения каждого элемента по умолчанию initializer ([class.mem]), используемые при инициализации.

A subexpression выражения e - это непосредственное подвыражение e или подвыражение непосредственного подвыражения e. [ Note: Выражения, встречающиеся в compound-statementa lambda-expression , не являются частями выражения lambda-expression. ]end note

А full-expression это

  • ан unevaluated operand,

  • а constant-expression,

  • an init-declaratorили a mem-initializer, включая составляющие выражения инициализатора,

  • вызов деструктора, сгенерированного в конце жизненного цикла объекта, отличного от a temporary object, или

  • выражение, которое не является частью другого выражения и не является частью полного выражения.

Если языковая конструкция определена для неявного вызова функции, использование языковой конструкции считается выражением для целей этого определения. Преобразования, применяемые к результату выражения для удовлетворения требований языковой конструкции, в которой появляется выражение, также считаются частью полного выражения. Для инициализатора выполнение инициализации объекта (включая оценку инициализаторов членов по умолчанию для агрегата) также считается частью полного выражения. [Example:

struct S {
  S(int i): I(i) { }       // full-expression is initialization of I
  int& v() { return I; }
  ~S() noexcept(false) { }
private:
  int I;
};

S s1(1);                   // full-expression is call of S​::​S(int)
void f() {
  S s2 = 2;                // full-expression is call of S​::​S(int)
  if (S(3).v())            // full-expression includes lvalue-to-rvalue and
                           // int to bool conversions, performed before
                           // temporary is deleted at end of full-expression
  { }
  bool b = noexcept(S());  // exception specification of destructor of S
                           // considered for noexcept
  // full-expression is destruction of s2 at end of block
}
struct B {
      B(S = S(0));
   };
   B b[2] = { B(), B() };  // full-expression is the entire initialization
                           // including the destruction of temporaries

end example]

[ Note: Оценка полного выражения может включать оценку подвыражений, которые лексически не являются частью полного выражения. Например, подвыражения, участвующие в оценке default arguments , считаются созданными в выражении, вызывающем функцию, а не в выражении, определяющем аргумент по умолчанию. ] end note

Чтение объекта, обозначенного a volatile glvalue, изменение объекта, вызов функции ввода-вывода библиотеки или вызов функции, которая выполняет любую из этих операций, - все side effectsэто изменения в состоянии среды выполнения. Evaluation выражения (или подвыражения) в целом включает в себя как вычисления значений (включая определение идентичности объекта для оценки glvalue и выборку значения, ранее присвоенного объекту для оценки prvalue), так и инициирование побочных эффектов. Когда вызов функции ввода-вывода библиотеки возвращается или оценивается доступ через изменчивое значение glvalue, побочный эффект считается завершенным, даже если некоторые внешние действия, подразумеваемые вызовом (например, сам ввод-вывод) или volatile доступом, могут еще не завершены.

Sequenced before представляет собой асимметричное, транзитивное, попарное отношение между оценками, выполняемыми одним thread, что приводит к частичному порядку среди этих оценок. Для любых двух вычислений A и B, если A выполняется последовательность до B (или, что то же самое, B есть sequenced after A), то выполнение A должно предшествовать выполнению B. Если A последовательность не была установлена ​​раньше B и B не была установлена ​​предыдущая A, то A и B являются unsequenced. [ Note: Выполнение неупорядоченных оценок может перекрываться. ] Оценки и относятся к случаям , когда последовательность либо предшествует, либо предшествует , но не указано, какая именно. [ Неопределенно последовательные оценки не могут перекрываться, но любая из них может быть выполнена первой. ] Выражение считается упорядоченным перед выражением, если каждое вычисление значения и каждый побочный эффект, связанный с выражением , упорядочен перед каждым вычислением значения и каждым побочным эффектом, связанным с выражением . end note A B indeterminately sequenced A B B ANote: end note X Y X Y

Каждое вычисление значения и побочный эффект, связанный с полным выражением, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным со следующим оцениваемым полным выражением.9

Если не указано иное, вычисления операндов отдельных операторов и подвыражений отдельных выражений неупорядочены. [ Note: В выражении, которое оценивается более одного раза во время выполнения программы, неупорядоченные и неопределенно упорядоченные вычисления его подвыражений не обязательно должны выполняться последовательно в разных вычислениях. ] Вычисления значений операндов оператора последовательно выполняются перед вычислением значения результата оператора. Если побочный эффект в a не упорядочен относительно другого побочного эффекта в той же ячейке памяти или вычисления значения с использованием значения любого объекта в той же ячейке памяти, а это не так , поведение не определено. [ В следующем разделе налагаются аналогичные, но более сложные ограничения на потенциально параллельные вычисления. ] end note memory location potentially concurrentNote: end note

[Example:

void g(int i) {
  i = 7, i++, i++;    // i becomes 9

  i = i++ + 1;        // the value of i is incremented
  i = i++ + i;        // the behavior is undefined
  i = i + 1;          // the value of i is incremented
}

end example]

При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента или с постфиксным выражением, обозначающим вызываемую функцию, упорядочиваются перед выполнением каждого выражения или оператора в теле вызываемая функция. Для каждого вызова функции F, для каждой оценки, A которая происходит внутри, F и каждой оценки B , которая не происходит внутри, F но оценивается в том же потоке и как часть одного и того же обработчика сигнала (если есть), либо A упорядочивается до, B либо B упорядочивается раньше A.10 [ Note: Если A и B не были бы иначе упорядочены, то они неопределенно упорядочены. ] Некоторые контексты в C ++ вызывают оценку вызова функции, даже если соответствующий синтаксис вызова функции отсутствует в единице перевода. [ Оценка вызывает одну или несколько функций выделения и конструктора; см . В другом примере вызов a может возникать в контекстах, в которых не появляется синтаксис вызова функции. ] Ограничения последовательности выполнения вызываемой функции (как описано выше) являются особенностями вызовов функций в том виде, в каком они оцениваются, независимо от синтаксиса выражения, вызывающего функцию. end noteExample: new-expression [expr.new] conversion function end example

Если обработчик сигнала выполняется в результате вызова std​::​raise функции, то выполнение обработчика упорядочивается после вызова std​::​raise функции и до ее возврата. [ Note: Когда сигнал получен по другой причине, выполнение обработчика сигнала обычно не упорядочено по отношению к остальной части программы. ] end note

Это положение иногда называют правилом «как если бы», потому что реализация может игнорировать любое требование настоящего международного стандарта, если результат такой, как если бы требование было соблюдено, насколько это можно определить по наблюдаемому поведению. программы. Например, фактическая реализация не должна оценивать часть выражения, если она может сделать вывод, что его значение не используется и что никаких побочных эффектов, влияющих на наблюдаемое поведение программы, не возникает.

Эта документация также включает условно поддерживаемые конструкции и поведение, зависящее от локали. Смотрите [intro.compliance].

Перегруженные операторы никогда не считаются ассоциативными или коммутативными.

Как указано в [class.temporary], после оценки полного выражения имеет место последовательность из нуля или более вызовов функций деструктора для временных объектов, обычно в обратном порядке построения каждого временного объекта.

Другими словами, выполнение функций не чередуется друг с другом.

4.7 Multi-threaded executions and data races [intro.multithread]

A thread of execution (также известный как a thread) - это единый поток управления в программе, включая начальный вызов конкретной функции верхнего уровня и рекурсивно включающий каждый вызов функции, впоследствии выполняемый потоком. [ Note: Когда один поток создает другой, первоначальный вызов функции верхнего уровня нового потока выполняется новым потоком, а не создающим потоком. ] Каждый поток в программе потенциально может получить доступ к каждому объекту и функции в программе. При размещенной реализации программа на C ++ может иметь более одного потока, выполняемого одновременно. Выполнение каждого потока продолжается, как определено в остальной части настоящего международного стандарта. Выполнение всей программы состоит из выполнения всех ее потоков. [ Обычно выполнение можно рассматривать как чередование всех его потоков. Однако некоторые виды атомарных операций, например, допускают выполнение, несовместимое с простым чередованием, как описано ниже. ] В автономной реализации определяется, может ли программа иметь более одного потока выполнения. end note11 Note: end note

Для обработчика сигнала, который не выполняется в результате вызова std​::​raise функции, не указано, какой поток выполнения содержит вызов обработчика сигнала.

Объект с автоматическим или потоком storage duration связан с одним конкретным потоком, и к нему может получить доступ другой поток только косвенно через pointer или reference.

4.7.1 Data races [intro.races]

Значение объекта, видимого потоку T в определенной точке, - это начальное значение объекта, значение, присвоенное Tобъекту другим потоком, или значение, присвоенное объекту другим потоком, в соответствии с приведенными ниже правилами. [ Note: В некоторых случаях вместо этого может быть неопределенное поведение. Большая часть этого раздела мотивирована желанием поддерживать атомарные операции с явными и подробными ограничениями видимости. Однако он также неявно поддерживает более простой вид для более ограниченных программ. ] end note

Два вычисления выражения, conflict если одно из них изменяет a, memory location а другое читает или изменяет одну и ту же ячейку памяти.

Библиотека определяет ряд atomic operations операций mutexes , которые специально определены как операции синхронизации. Эти операции играют особую роль в том, чтобы сделать назначения в одном потоке видимыми для другого. Операция синхронизации в одной или нескольких ячейках памяти является либо операцией потребления, либо операцией получения, либо операцией освобождения, либо одновременно операцией получения и освобождения. Операция синхронизации без связанной ячейки памяти является ограничением и может быть либо ограничением получения, либо ограничением разблокировки, либо одновременно ограничением захвата и разблокировки. Кроме того, существуют упрощенные атомарные операции, которые не являются операциями синхронизации, и атомарные операции чтения-изменения-записи, которые имеют особые характеристики. [ Note: Например, вызов, который получает мьютекс, будет выполнять операцию получения в местах, составляющих мьютекс. Соответственно, вызов, освобождающий тот же мьютекс, выполнит операцию освобождения в тех же местах. Неформально выполнение операции освобождения A заставляет предыдущие побочные эффекты в других ячейках памяти становиться видимыми для других потоков, которые позже выполняют операцию потребления или получения A. «Расслабленные» атомарные операции не являются операциями синхронизации, хотя, как и операции синхронизации, они не могут участвовать в гонке данных. ]end note

Все модификации конкретного атомарного объекта M происходят в определенном общем порядке, называемом modification order of M. [ Note: Для каждого атомарного объекта существует отдельный заказ. Не требуется, чтобы их можно было объединить в единый общий заказ для всех объектов. В общем, это будет невозможно, поскольку разные потоки могут наблюдать модификации разных объектов в несовместимом порядке. ]end note

release sequence Во главе с операцией освобождения A на атомном объекте M является максимальной смежным субом-последовательность побочных эффектов в порядке модификации M, где первая операция является A, и каждой последующей операция

  • выполняется тем же потоком, который выполнял A, или

  • это атомарная операция чтения-изменения-записи.

Определенная библиотека вызывает synchronize with другие вызовы библиотеки, выполняемые другим потоком. Например, атомарное освобождение хранилища синхронизируется с получением загрузки, которое берет свое значение из store ([atomics.order]). [ Note: За исключением указанных случаев, чтение более позднего значения не обязательно обеспечивает видимость, как описано ниже. Такое требование иногда мешает эффективной реализации. ] [ Спецификации операций синхронизации определяют, когда один читает значение, записанное другим. Для атомарных объектов определение ясное. Все операции с данным мьютексом выполняются в едином общем порядке. Каждое получение мьютекса «считывает значение, записанное» последним выпуском мьютекса. ] end noteNote: end note

От оценки A carries a dependency к оценке, B если

  • значение A используется как операнд B, если:

    или

  • A записывает скалярный объект или битовое поле M, B читает значение, записанное A из M, и A упорядочивается до B, или

  • для некоторой оценки X, A несет в себе зависимость к X, и X несет в себе зависимость к B.

[ Note: «Несет зависимость от» - это подмножество «упорядочено до» и аналогично строго внутри потока. ] end note

Оценка A - dependency-ordered before это оценка, B если

  • A выполняет операцию выпуска над атомарным объектом M, а в другом потоке B выполняет операцию потребления M и считывает значение, записанное любым побочным эффектом в последовательности выпуска, озаглавленной A, или

  • для некоторой оценки X, A упорядочивается по зависимостям раньше X и X несет в себе зависимость от B.

[ Note: Отношение «предварительно упорядочено зависимостями» аналогично «синхронизируется с», но использует освобождение / потребление вместо выпуска / получения. ]end note

Оценка A inter-thread happens before оценка, B если

  • A синхронизируется с B, или

  • A упорядочивается по зависимости раньше B, или

  • для некоторой оценки X

    • A синхронизируется с предыдущим X и X упорядочивается B, или

    • A упорядочивается до, X а X межпоточное происходит раньше B, или

    • A inter-thread происходит раньше, X а X inter-thread - раньше B.

[ Note: Отношение «между потоками происходит до» описывает произвольные конкатенации отношений «упорядочено до», «синхронизируется с» и «упорядочено по зависимости до» с двумя исключениями. Первое исключение состоит в том, что конкатенация не может заканчиваться словами «в порядке зависимости до», за которым следует «упорядочено до». Причина этого ограничения заключается в том, что операция потребления, участвующая в соотношении «зависимость упорядочена до», обеспечивает упорядочение только в отношении операций, от которых эта операция потребления фактически несет зависимость. Причина, по которой это ограничение применяется только к концу такой конкатенации, состоит в том, что любая последующая операция освобождения будет обеспечивать необходимое упорядочение для предыдущей операции потребления. Второе исключение состоит в том, что конкатенация не может состоять полностью из «предшествующей последовательности». Причины этого ограничения: (1) разрешить транзитивное закрытие «межпотока происходит до» и (2) отношение «происходит до», определенное ниже, предусматривает отношения, полностью состоящие из «упорядочено до». ] end note

Оценка A happens before оценка B (или, что то же самое, B happens after A), если:

  • A стоит раньше B, или

  • A межпоток происходит раньше B.

Реализация должна гарантировать, что выполнение программы не демонстрирует цикл в отношении «произошло до». [ Note: В противном случае этот цикл был бы возможен только при использовании операций потребления. ] end note

Оценка A strongly happens before оценка, B если

  • A стоит раньше B, или

  • A синхронизируется с B, или

  • A сильно бывает до X и X сильно бывает до B.

[ Note: В отсутствие операций потребления, то происходит раньше и строго происходит до того, как отношения идентичны. Сильно бывает до того, как существенно исключает операции потребления. ]end note

visible side effect A На объект скалярного или битового поля M относительно вычисления значения B из M удовлетворяет условиям:

  • A происходит раньше B и

  • нет никакой другой побочный эффект , X чтобы M таким образом, что A происходит до X и X происходит до B.

Значение неатомарного скалярного объекта или битового поля M, определяемое оценкой B, должно быть значением, сохраненным видимым побочным эффектом A. [ Note: Если есть неопределенность в отношении того, какой побочный эффект для неатомарного объекта или битового поля является видимым, то поведение либо не определено, либо не определено. ] [ Это означает, что операции с обычными объектами не меняются явно. На самом деле это невозможно обнаружить без гонок данных, но необходимо гарантировать, что гонки данных, как определено ниже, и с соответствующими ограничениями на использование атомики, соответствуют гонкам данных в простом чередующемся (последовательно согласованном) выполнении. ] end noteNote: end note

Значение атомарного объекта M, определяемое оценкой B, должно быть значением, сохраненным некоторым побочным эффектом, A который модифицируется M, чего B не было раньше A. [ Note: Набор таких побочных эффектов также ограничен остальными правилами, описанными здесь, и, в частности, требованиями согласованности ниже. ]end note

Если операция A , изменяющая атомарный объект, M происходит до операции, B которая изменяет M, то она A должна быть раньше, чем B в порядке модификации M. [ Note: Это требование известно как согласованность записи-записи. ] end note

Если значение вычисление A атомного объекта M происходит до вычисления значения B из M, и A принимает значение от побочного эффекта X на M, то значение вычисляется путем B должно быть либо значение хранится X или значение , сохраненное с помощью побочного эффекта Y на M, где Y следует X в порядок модификации M. [ Note: Это требование известно как согласованность чтения-чтения. ] end note

Если вычисление A значения атомарного объекта M происходит до операции, B которая модифицирует M, тогда A оно должно принимать свое значение из побочного эффекта X на M, где X предшествует B в порядке модификации M. [ Note: Это требование известно как согласованность чтения-записи. ] end note

Если побочный эффект X на атомном объекте M происходит до вычисления значения B из M, то оценка B принимает свое значение от X или от побочного эффекта , Y который следует X в порядке модификации M. [ Note: Это требование известно как согласованность записи-чтения. ] end note

[ Note: Четыре предшествующих требования согласованности эффективно запрещают компилятор переупорядочивать атомарные операции для одного объекта, даже если обе операции являются ослабленными нагрузками. Это фактически обеспечивает гарантию согласованности кэша, предоставляемую большинством оборудования, доступного для атомарных операций C ++. ]end note

[ Note: Значение, наблюдаемое при загрузке атома, зависит от отношения «произошло раньше», которое зависит от значений, наблюдаемых при загрузке атома. Предполагаемое прочтение состоит в том, что должна существовать ассоциация атомарных нагрузок с модификациями, которые они наблюдают, что вместе с соответствующим образом выбранными порядками модификации и отношением «происходит до», полученным, как описано выше, удовлетворяют результирующим ограничениям, как наложено здесь. ] end note

Два действия - potentially concurrent если

  • они выполняются разными потоками, или

  • они не упорядочены, по крайней мере один из них выполняется обработчиком сигнала, и оба они не выполняются одним и тем же вызовом обработчика сигналов.

Выполнение программы содержит, data race если она содержит два потенциально одновременных конфликтующих действия, по крайней мере одно из которых не является атомарным, и ни одно из них не происходит раньше другого, за исключением особого случая для обработчиков сигналов, описанного ниже. Любая такая гонка данных приводит к неопределенному поведению. [ Note: Можно показать, что программы, которые правильно используют мьютексы и memory_­order_­seq_­cst операции для предотвращения всех гонок данных и не используют никаких других операций синхронизации, ведут себя так, как если бы операции, выполняемые составляющими их потоками, просто чередовались, при этом каждое вычисление значения объекта берется из последнего побочный эффект на этот объект в этом чередовании. Обычно это называют «последовательной согласованностью». Однако это относится только к программам без гонки данных, а программы без гонки данных не могут наблюдать большинство программных преобразований, которые не изменяют семантику однопоточной программы. Фактически, большинство однопоточных программных преобразований по-прежнему разрешены, поскольку любая программа, которая в результате ведет себя иначе, должна выполнять неопределенную операцию. ] end note

Два доступа к одному и тому же объекту типа volatile std​::​sig_­atomic_­t не приводят к гонке данных, если оба происходят в одном потоке, даже если один или несколько происходит в обработчике сигнала. Для каждого вызова обработчика сигнала оценки, выполняемые потоком, вызывающим обработчик сигнала, могут быть разделены на две группы, A и B, таким образом, никакие оценки не B происходят до оценок в A, и оценки таких volatile std​::​sig_­atomic_­t объектов принимают значения, как если бы все оценки в A произошли до выполнения. обработчика сигнала и выполнение обработчика сигнала произошло до всех вычислений в B.

[ Note: Преобразования компилятора, которые вводят назначения для потенциально разделяемой области памяти, которая не может быть изменена абстрактной машиной, как правило, запрещены настоящим международным стандартом, поскольку такое назначение может перезаписать другое назначение другим потоком в случаях, когда выполнение абстрактной машины не сталкивались с гонкой данных. Это включает в себя реализации назначения элементов данных, которые перезаписывают соседние элементы в отдельных ячейках памяти. Изменение порядка атомных нагрузок в случаях, когда рассматриваемые атомы могут быть псевдонимами, также обычно не допускается, поскольку это может нарушать правила согласованности. ] end note

[ Note: Преобразования, которые вводят спекулятивное чтение потенциально разделяемой области памяти, могут не сохранять семантику программы C ++, как определено в этом международном стандарте, поскольку они потенциально вызывают гонку данных. Однако они обычно действительны в контексте оптимизирующего компилятора, который нацелен на конкретную машину с четко определенной семантикой для гонок данных. Они были бы недействительны для гипотетической машины, которая не терпит гонок или обеспечивает обнаружение гонок на оборудовании. ] end note

4.7.2 Forward progress [intro.progress]

Реализация может предполагать, что любой поток в конечном итоге выполнит одно из следующих действий:

  • прекратить,

  • выполнить вызов функции ввода-вывода библиотеки,

  • выполнить доступ через изменчивое значение glvalue, или

  • выполнить операцию синхронизации или атомарную операцию.

[ Note: Это сделано для того, чтобы позволить преобразования компилятора, такие как удаление пустых циклов, даже если завершение не может быть доказано. ] end note

Выполнение элементарных функций, которые либо определены как есть, lock-free либо указаны как lock-free есть lock-free executions.

  • Если есть только один поток, которого нет blocked в стандартной библиотечной функции, выполнение в этом потоке без блокировки должно завершиться. [ Note: Одновременное выполнение потоков может помешать продвижению безблокирующего выполнения. Например, такая ситуация может возникнуть с реализациями условного хранилища с блокировкой загрузки. Это свойство иногда называют свободным от препятствий. ] end note

  • Когда одно или несколько запусков без блокировки выполняются одновременно, по крайней мере одно должно завершиться. [ Note: Для некоторых реализаций трудно обеспечить абсолютные гарантии этого эффекта, поскольку повторяющиеся и, в частности, неподходящие помехи со стороны других потоков могут препятствовать продвижению вперед, например, путем многократного перехвата строки кэша для несвязанных целей между инструкциями с блокировкой загрузки и условными сохранениями. Реализации должны гарантировать, что такие эффекты не могут на неопределенное время задерживать прогресс в ожидаемых рабочих условиях, и что такие аномалии, следовательно, могут безопасно игнорироваться программистами. Вне этого документа это свойство иногда называют свободным от блокировки. ] end note

Во время выполнения потока выполнения каждое из следующего называется execution step:

  • завершение потока исполнения,

  • выполнение доступа через изменчивое значение glvalue, или

  • завершение вызова функции ввода-вывода библиотеки, операции синхронизации или атомарной операции.

Вызов стандартной библиотечной функции, которая, blocks как считается, непрерывно выполняет шаги выполнения, ожидая выполнения условия, которое она блокирует. [ Example: Библиотечная функция ввода-вывода, которая блокируется до завершения операции ввода-вывода, может рассматриваться для непрерывной проверки того, завершена ли операция. Каждая такая проверка может состоять из одного или нескольких этапов выполнения, например, с использованием наблюдаемого поведения абстрактной машины. ]end example

[ Note: Из-за этого и предыдущего требования относительно того, какие потоки выполнения должны выполняться в конечном итоге, отсюда следует, что ни один поток выполнения не может выполняться вечно без выполнения шага выполнения. ]end note

Поток выполнения, makes progress когда происходит этап выполнения или выполнение без блокировки не завершается, потому что есть другие параллельные потоки, которые не заблокированы в стандартной библиотечной функции (см. Выше).

Для потока выполнения concurrent forward progress guaranteesреализация гарантирует, что поток в конечном итоге будет прогрессировать до тех пор, пока он не завершится. [ Note: Это требуется независимо от того, выполнялись или нет другие потоки выполнения (если есть). В конечном итоге выполнение этого требования означает, что это произойдет в неопределенный, но конечный период времени. ]end note

Это определяется реализацией, обеспечивают ли созданные реализациейmain потоки выполнения, которые выполняются, и потоки выполнения, созданные с помощью std​::​thread параллельных гарантий продвижения вперед. [ Note: Рекомендуется, чтобы реализации общего назначения предоставляли эти гарантии. ]end note

Для потока выполнения parallel forward progress guaranteesреализация не требуется, чтобы гарантировать, что поток в конечном итоге будет прогрессировать, если он еще не выполнил ни одного шага выполнения; после того, как этот поток выполнил шаг, он предоставляет гарантии одновременного продвижения вперед.

[ Note: Здесь не указывается требование о том, когда запускать этот поток выполнения, которое обычно указывается сущностью, создающей этот поток выполнения. Например, поток выполнения, который обеспечивает одновременные гарантии продвижения вперед и выполняет задачи из набора задач в произвольном порядке, одну за другой, удовлетворяет требованиям параллельного продвижения вперед для этих задач. ]end note

Для потока выполнения weakly parallel forward progress guaranteesреализация не гарантирует, что поток в конечном итоге будет прогрессировать.

[ Note: Нельзя ожидать, что потоки выполнения, обеспечивающие слабо параллельные гарантии продвижения вперед, будут прогрессировать, независимо от того, работают ли другие потоки или нет; однако блокировка с делегированием гарантии продвижения вперед, как определено ниже, может использоваться для обеспечения того, чтобы такие потоки выполнения в конечном итоге достигли прогресса. ]end note

Гарантии одновременного продвижения вперед сильнее, чем гарантии параллельного продвижения вперед, которые, в свою очередь, сильнее, чем гарантии слабо параллельного продвижения вперед. [ Note: Например, некоторые виды синхронизации между потоками выполнения могут выполняться только в том случае, если соответствующие потоки выполнения обеспечивают параллельные гарантии продвижения вперед, но не смогут достичь прогресса при слабо параллельных гарантиях. ]end note

Когда поток выполнения P указывается для block with forward progress guarantee delegation завершения набора S потоков выполнения, то в течение всего времени P блокировки Sреализация должна гарантировать, что гарантии продвижения вперед, предоставляемые по крайней мере одним потоком выполнения в S , по крайней мере столь же сильны, как Pи гарантии продвижения вперед. [ Note: Не указано, какой поток или потоки выполнения S выбраны и для какого количества шагов выполнения. Усиление не является постоянным и не обязательно действует до конца жизненного цикла затронутого потока выполнения. Пока P он заблокирован, реализация должна в конечном итоге выбрать и потенциально усилить поток выполнения в S. ] Как только поток выполнения в in завершается, он удаляется из . Когда-то пусто, разблокировано.end note S S S P

[ Таким образом, Note: поток выполнения B может временно обеспечить более надежную гарантию продвижения вперед на определенное время из-за того, что второй поток выполнения A блокируется на нем с делегированием гарантии продвижения вперед. В свою очередь, если B затем блоки с включенной гарантией продвижения вперед C, это также может временно обеспечить более сильную гарантию продвижения вперед C. ]end note

[ Note: Если все потоки выполнения находятся на стадии S завершения (например, они завершаются и не используют блокирующую синхронизацию неправильно), то Pвыполнение операции, которая блокируется с делегированием гарантии выполнения вперед, не приведет Pк эффективному ослаблению гарантии выполнения. ]end note

[ Note: Это не снимает каких-либо ограничений, касающихся блокировки синхронизации для потоков выполнения, обеспечивая параллельные или слабо параллельные гарантии продвижения вперед, потому что реализация не требуется для усиления конкретного потока выполнения, слишком слабая гарантия выполнения которого препятствует общему прогрессу. ]end note

Реализация должна гарантировать, что последнее значение (в порядке модификации), присвоенное атомарной операцией или операцией синхронизации, станет видимым для всех других потоков в течение конечного периода времени.

4.8 Acknowledgments [intro.ack]

Язык программирования C ++, описанный в этом документе, основан на языке, описанном в главе R (Справочное руководство) Страуструпа: The C++ Programming Language (второе издание, издательство Addison-Wesley Publishing Company, ISBN 0-201-53992-6, авторское право © 1991 AT&T) . Это, в свою очередь, основано на языке программирования C, как описано в Приложении А Кернигана и Ричи: The C Programming Language (Prentice-Hall, 1978, ISBN 0-13-110163-3, авторское право © 1978 AT&T).

Части библиотечных пунктов этого документа основаны на работе PJ Plauger, которая была опубликована как The Draft Standard C++ Library (Prentice-Hall, ISBN 0-13-117003-1, авторское право © 1995 PJ Plauger).

POSIX® является зарегистрированным товарным знаком Института инженеров по электротехнике и электронике, Inc.

ECMAScript® - зарегистрированная торговая марка Ecma International.

Все права на эти оригиналы защищены.