как будто в языковых стандартах

Каково точное значение фразы «как будто» в стандарте и как она работает, когда пользователь может изменять отдельные части поведения.

Вопрос касается стандарта С++, когда речь идет о версии nothrow operator new. 18.4.1.1/7 гласит (выделено мной):

Эта невозвратная версия оператора new возвращает указатель, полученный как если бы полученный из обычной версии.

Насколько я понимаю, «как если бы» не требует конкретной реализации, если поведение является подходящим. Итак, если operator new был реализован так (я знаю, что это несовместимая реализация, так как нет цикла или использования new_handler, но я сокращаю это, чтобы сосредоточиться на моей проблеме):

// NOTE - not fully compliant - for illustration purposes only.
void *operator new(std::size_t s)
{
    void *p = malloc(s);
    if (p == 0)
        throw std::bad_alloc();
    return p;
}

Тогда было бы законно написать версию nothrow следующим образом:

// NOTE - not fully compliant - for illustration purposes only.
void *operator new(std::size_t s, const std::nothrow_t &nt)
{
    return malloc(s);
}

Но допустим, программа заменяет operator new, чтобы использовать какой-то другой распределитель. Означает ли «как если бы» компилятор автоматически изменил поведение версии nothrow, чтобы использовать этот другой распределитель? Обязан ли разработчик заменить как обычную, так и нулевую версии?


person R Samuel Klatchko    schedule 21.02.2010    source источник
comment
gotw.ca/publications/mill15.htm gotw.ca/publications/mill16.htm   -  person Nikolai Fetissov    schedule 21.02.2010
comment
В последнем черновике C++0x была изменена формулировка: эта версия оператора new с нулевой подачей возвращает указатель, полученный, как если бы он был получен из (возможно, замененной) обычной версии. И поведение по умолчанию изменилось на вызов оператора new(size). Если вызов возвращается нормально, возвращает результат этого вызова. В противном случае возвращает нулевой указатель.   -  person Johannes Schaub - litb    schedule 21.02.2010


Ответы (3)


Из 1.9" Выполнение программы:

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

и в информационной сноске:

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

В стандарте особо отмечается, что требование «как если бы» является обязательным для заменяющей версии версии operator new(). Однако, как я читал, это требование ложится на программиста, переопределяющего operator new(), а не на компилятор. Обратная сторона этой ответственности заключается в том, что я думаю, что стандарт в значительной степени требует, чтобы реализация nothrow operator new() по умолчанию, предоставляемая библиотекой, должна делать что-то вроде вызова бросающего new в try/catch и возвращать 0, если std::bad_alloc перехватывается.

Где «как если бы правило» могло бы сыграть роль, так это в том случае, если бы компилятор/компоновщик/что-то еще были достаточно умны, чтобы понять, что когда использовалось бросающее new() по умолчанию, не бросающее new() по умолчанию могло бы использовать ярлык, но если бы выбрасывающий по умолчанию new() был переопределен, не бросающий по умолчанию new() должен был бы действовать по-другому. Я уверен, что это технически возможно для реализации (даже если вы, вероятно, не можете выразить это на стандартном C++). Я был бы удивлен, если бы когда-либо была реализация, которая делала это.

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

person Michael Burr    schedule 21.02.2010
comment
Я не знаю, почему в моей первой версии ответа говорилось о требуемом поведении operator delete(), поскольку в вашем вопросе об этом ничего не говорилось. Слишком мало спал прошлой ночью или что-то в этом роде. - person Michael Burr; 21.02.2010
comment
по поводу вашего комментария, начинающегося с оборотной стороны..., что еще есть в стандарте, что потребовало бы по умолчанию nothrow operator new для вызова метательного operator new? - person R Samuel Klatchko; 21.02.2010
comment
Я не уверен, что есть что-то еще - то, что я пытался донести, - это то, что я думаю, что это предотвращает использование по умолчанию nothrow new() для быстрого вызова вызова malloc() (или что-то еще, вызывающее new() по умолчанию напрямую. - person Michael Burr; 21.02.2010
comment
Я читал это так, как будто это только подтверждает, что он привязывается также к замещающей версии (поскольку он не говорит, что только привязывается к замещающей версии). На самом деле без этого утверждения он будет привязан как к замене, так и к реализации по умолчанию. Так что, возможно, only каким-то образом подразумевается, и тем самым он говорит, что он не применяется к реализации по умолчанию (поскольку в противном случае утверждение было бы в значительной степени избыточным). Точно сказать не могу :) - person Johannes Schaub - litb; 21.02.2010

Если изменение распределителя в operator new приводит к заметным изменениям в поведении совместимой программы на C++, то да, это может потребовать изменения реализации версии no-throw. В частности, если operator delete ожидает только блоки, выделенные новым распределителем, то no-throw новый должен измениться.

Насколько я понимаю, использование как если бы допускает такую ​​реализацию, как ваша, когда пользователь не переопределил стандартный operator new. Как только он это сделает, реализация не должна использовать no-throw operator new на основе malloc и должна либо явно вызывать объявленную пользователем версию, либо, по крайней мере, повторно использовать достаточное количество объявленной пользователем версии, чтобы соответствующая программа нельзя сказать, что версия без броска была реализована не так.

person CB Bailey    schedule 21.02.2010
comment
Если я правильно понимаю, вы предполагаете, что компилятор несет ответственность за обеспечение согласованности между глобальным оператором new и глобальным nothrow new, когда пользователь заменил только один из них. Это может быть довольно обременительным для компилятора, особенно если ему нужно повторно использовать достаточное количество заявленной пользователем версии. Если бы это было намерением, возможно, в стандарте следовало бы указать, что глобальный оператор new реализуется в терминах nothrow new и не может быть заменен — пользователю было бы разрешено переопределять только глобальный nothrow new. - person Dan; 21.02.2010
comment
я думаю, что nothrow просто должен try { ... } вызвать обычный оператор new, чтобы предотвратить побег исключений. Это, однако, означало бы, что обычное новое не может назвать совершенно новым. Странный. :) - person Johannes Schaub - litb; 21.02.2010
comment
@Dan: все наоборот - стандарт настоятельно предполагает, что nothrow new реализуется с точки зрения создания нового. Я не думаю, что есть какое-то намерение, чтобы это было каким-то другим способом, хотя, если ни один из них не заменен, то есть очевидная оптимизация nothrow. Чарльз или, по крайней мере, только указывает, что, возможно, реализация могла бы вывернуться из этого с помощью некоторых причудливых движений. Тот факт, что эта причудливая работа ног очень сложна, не является недостатком стандарта, поскольку это то, что реализация, вероятно, никогда не захочет делать. Просто позвоните пользователю, бросающему новый. - person Steve Jessop; 21.02.2010
comment
Я сделал тест: void *operator new(size_t p) throw(bad_alloc) { printf("#"); return malloc(p); } int main() { printf("|"); delete new (nothrow) int; } . На самом деле, GCC не выводит # после последнего |. Таким образом, это не будет соответствовать нашей интерпретации, поскольку оно как бы игнорирует важное наблюдаемое поведение. - person Johannes Schaub - litb; 21.02.2010
comment
Тогда я предполагаю, что, вероятно, стандарт означает, что по умолчанию nothrow new выделяет, как будто по умолчанию new, а не по умолчанию nothrow new выделяет, как если бы текущий новый. Что тоже достаточно справедливо. - person Steve Jessop; 22.02.2010

Разработчик должен заменить как обычную версию, так и версию без вывода. Прочтите эту статью на GOTW.

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

person Dan    schedule 21.02.2010
comment
@Dan - в статье GOTW конкретно говорится о предоставлении полного набора замен для специфического для класса оператора new. Я полагаю, что проблема, которая беспокоит Херба Саттера, заключается в том, что если вы переопределяете оператор new для класса, вы не скроете оператор new, если вы также не переопределите его. - person R Samuel Klatchko; 21.02.2010
comment
Хороший вопрос, я пропустил, что вы спрашивали о новых глобальных операторах. Последний пункт перед резюме может иметь отношение к вашему вопросу: Саттер предполагает, что глобальный nothrow new должен обеспечивать согласованную семантику с обычным new. - person Dan; 21.02.2010