Re[109]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 12:03
Оценка:
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, vopl, Вы писали:


V>>Приведена выдержка из стандарта, в которой определяется что суть есть "object creation". И как после нее можно заявлять о потере смысла? Она и определяет смысл. Она есть смысл.


S>Или проблема в том, что все это раскидано в разных кусках стандарта. Которые нужно увязать друг с другом, чтобы получить общую картину.


V>>Какой еще lifetime Приведено оригинальное определение. В нем нет ни слова про lifetime.


S>Или нужно просто посмотреть на чуть более расширенную цитату (https://timsong-cpp.github.io/cppwp/basic.memobj#intro.object-1):

S>

The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object is created by a definition, by a new-expression ([expr.new]), by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union, or when a temporary object is created ([conv.rval], [class.temporary]). An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction ([class.cdtor]).

S>[Note 1: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. — end note]

S>The properties of an object are determined when the object is created. An object can have a name ([basic.pre]). An object has a storage duration ([basic.stc]) which influences its lifetime ([basic.life]). An object has a type ([basic.types]).

S>[Note 2: Some objects are polymorphic ([class.virtual]); the implementation generates information associated with each such object that makes it possible to determine that object's type during program execution. — end note]


S>Т.е. в разделе про создание объектов уже ссылаются на lifetime.


Cсылаются на lifetime не в разделе про создание объектов. Cсылаются на lifetime в разделе Object model. В этом разделе говорится,
что делается с объектами в программе
что такое создание объекта (вот это есть определение создания объекта. И тут lifetime-а нет)
как объект связан со storage и какие этапы там с ним происходят (а вот тут уже есть lifetime. Но это не про создание объекта, это его другой аспект)

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

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

Мир Дружба Жвачка
Re[110]: Когда это наконец станет defined behavior?
От: so5team https://stiffstream.com
Дата: 22.08.23 12:13
Оценка:
Здравствуйте, vopl, Вы писали:

V>что такое создание объекта (вот это есть определение создания объекта. И тут lifetime-а нет)


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

Т.е. эта фраза перечисляет легальные способы возникновения объектов в программе. Типа если объект появился в результате одного действия из перечисленных, то он считается созданным.

Собственно, как раз то, что раньше объекты могли возникать в программе нелегально с точки зрения стандарта, и пришлось вводить такие вещи, как implicit lifetime types, implicitly creates в купе с std::start_lifetime_as и т.д.

Опять же это все к тому, что стандарт написан специальным языком для специалистов и для специфических целей.
Re[106]: Когда это наконец станет defined behavior?
От: vdimas Россия  
Дата: 22.08.23 12:19
Оценка:
Здравствуйте, vopl, Вы писали:

V>глобальность непричем, вот такой кейс имеет то же самое "гипотетическое UB"

V>
V>struct T {int m;};

V>T make()
V>{
V>    T inner;
V>    inner.m = 220;
V>    return inner;
V>}

V>int main()
V>{
V>    const T outer = make();

V>    return 0;
V>}


В этом случае не получится ни объяснить это UB, ни продемонстрировать потенциальный кейз, где UB может себя проявить.
Оно возможно только в фазе неоконченной инициализации модуля, но на такое UB можно попасть и при простом статическом const int i = calcValue();


V>>Для локальных констант оно невоспроизводимо, т.к. доступ к константе происходит только после инициализации переменной.

V>не важно когда происходит доступ.

Важно, согласно определению lifetime, да и просто здравого смысла.


V>Тип объекта, определяемый декларацией потенциально является константным.


Но что-то выполнить в коде и пронаблюдать потенциальное неопределённое поведение можно только после объявления константы, где объявление локальной константы по стандарту совмещено с определением.
Этот момент и не даёт продемонстрировать неопределённость на практике.

Исключение составляет extern const, но это опять заход на новый круг про глобальные переменные. ))


V>На этой основе компилятор может делать всякие предположения относительно доступа к этому объекту. И объект после своего создания/инициализации используется как мутабельный.


В твоём примере объект outer считается не созданным до возврата из make.


V>вот так оно может быть развернуто компилятором

V>

V>struct T {int m;};

V>int main()
V>{
V>    count T outer{};// только конструктор будет от неконстантного имени. Да я знаю что конструктору плевать, просто отмечаю сей факт

V>    T& inner = const_cast<T&>(outer);
V>    inner.m = 220;

V>    return 0;
V>}


Не может и не будет. ))

Там будет другое:
— sub RSP на необходимую величину для размещения всех локальных переменных;
— присвоить регистру EDI адрес outer (допустим, значение RSP+16);
— call make с параметром в регистре EDI;
— считать outer инициализированным.

А если указать флаг оптимизации, то будет простое использование константы 220, безо-всяких переменных, объектов и ф-ий.


V>наверное да.. Я игнорирую тезисы которые просто высказываются в утвердительной форме, без обоснований


Основание можно спросить, т.к. вам тут многое говорится как "и так известное", и окружающие ХЗ что именно непонятно.


V>>Что характерно, в статической фазе константе const int i будет присвоено значение 0, а в динамической фазе эта константа будет перезаписана результатом вызова calValue(). Не смущает, ы? ))

V>это все не важно

Было бы не важно, но вы пока мест показали именно такое UB, для демонстрации которого не требовался ни RVO/NRVO, ни даже нетривиальные типы данных.
Достаточно было простого const int i = calcValue();, и сей UB живёт с самых первых версий С++ 80-х годов.
И будет жить вечно, походу. ))

И такие вещи упоминаются как сами собой разумеющиеся, т.е. не требующие обоснования, бо относятся к минимальному бэкгрраунду для С++.


V>>Так вот, услышьте уже, что доступ к неинициализированным данным модуля является UB и безо-всякого RVO/NRVO, т.е. это другое UB, нерелевантное обсуждаемому.

V>Услышал. Сабжевое "гипотетическое UB" — не про доступ к неинициализированным данным.

Но показать не можете. ))
Пока что была "демонстрация" только для глобальный переменной.

А для локальной ты никак не перепрыгнешь обязательность инициализации константы при объявлении.


V>В кейсе с "гипотетическим UB" все инициализировано до использования.


До использования константной переменной.
Ты всерьёз думаешь, что никто не понял ваш аргумент про "фактическое слияние" одного объекта с другим?

Любая фактическая подмена, которая не нарушает семантику, является "не важной" для рассуждений, до тех пор, пока язык даёт свои гарантии.
А он их даёт, т.к. переменная outer может быть использована только после того, как переменная inner закончила свой жизненный цикл.

А тот факт, что обе переменные обозначали одну и ту же область памяти — это и есть суть RVO оптимизации (не обязательно, кстате, было заострять на более узком кейзе NRVO, бо оно попахивало очередным непониманием материала).


V>Это был пример в котором объявлен константный объект, он создан/инициализирован


Не создан и не инициализирован.
Более того, мог быть создан объект совсем другого типа!

Простой пример:
#include <iostream>

struct Base {
    int value;
    char filler[256];
};

Base make() {
    Base result;
    result.value = 42;
    return result;
}

struct Derived : Base {
    Derived(const Base & other) {
        value = other.value;
    }
};

struct SomeObj {
    int value;

    SomeObj(const Base & other) {
        value = other.value;
    }
};

int main() {
    const Base outer1 = make();
    std::cout << outer1.value;

    const Derived outer2 = make();
    std::cout << outer2.value;

    const SomeObj outer3 = make();
    std::cout << outer3.value;

    return 0;
}


Для всех 3-х объектов будет идентичный код в случае инлайна конструтора копирования от Base.


V>и потом с ним идет работа как не с константным, что есть UB.


Наоборот, сначала идёт работа как с неконстантным, а потом этот объект становится доступным для операций на вызывающей стороне, приобретая константность.
У тебя нет возможность использовать переменную outer до возврата из make.


V>>Но после динамической фазы инициализации модуля, либо же, в этой фазе, но с учётом гарантированной последовательности инициализации глобальных переменных модуля, можно избежать упомянутого мною UB, и RVO/NRVO опять на него никак не влияют. Всё.

V>Не в ту сторону все.

А ничего другого, показывающего некий UB, вы так и не показали.


V>Пример посмотрел, понял, там отписал. В нем другой способ инициирования записи в константный объект, в общем, этот пример не принимается как полный, но очень хорошо показывает запись в константный объект, которая есть и в кейсе с "гипотетическим UB"


Вообще-то, тот пример намекал на возможность подобной утечки и через buildMap(), если адрес локальной переменной result куда-то протечёт как неконстантный.

Но! Утекание адреса локальной переменной является UB еще до рассждений об RVO, такое утекание — грубейшая ошибка!
Не везёт вам... ))

Итого, мы упомянули уже три известных UB:

1. UB с доступом к неинициализированному значению при инициализации глобальных переменных.
Решение: обращаться к глобальным переменным модуля в фазе динамической инициализации необходимо согласно гарантированному порядку их инициализации.
К переменным из других модулей можно обращаться только по окончании фазы динамической инициализации (т.е. после вызова main)

2. UB с константностью при наличии побочных эффектов в конструкторах.
Решение:
— такие объекты не стоит делать константами;
— такие объекты не стоит делать перемещаемыми/копируемыми.

3. UB при утекании адресов/ссылок на локальные переменные.
Решение: не давать им утекать, это грубая ошибка.

Но не видели еще работающего примера с демонстрацией UB вокруг константности, вызванного исключительно RVO!
Т.е. когда о любых других UB речи не идёт.

Итак, пример будет?
(желательно с объяснением, конечно, бо упомянутые 3 UB объяснить проще простого)
Отредактировано 23.08.2023 11:56 vdimas . Предыдущая версия . Еще …
Отредактировано 22.08.2023 12:23 vdimas . Предыдущая версия .
Отредактировано 22.08.2023 12:21 vdimas . Предыдущая версия .
Re[106]: Когда это наконец станет defined behavior?
От: vdimas Россия  
Дата: 22.08.23 12:31
Оценка:
Здравствуйте, vopl, Вы писали:

V>Да штош такое то) Сабжевый наш UB — НЕ имеет природу недоинициализированных глобальных переменных в динамической фазе инициализации.


Пока что был только такой работающий пример от σ, и только этот пример можно было предметно пороть.

Твои вариации с локальным константным объектом никакого потенциального UB не несут, обсуждать нечего.


V>Он про запист в гипотетически константный, нормально проинициализированный объект.


Ну так доступ к этому константному объекту уже есть или еще нет? ))

Как ты можешь продемонстрировать "неопределённое поведение" константного объекта в отсутствии доступа к целевому константному объекту?
Нет поведения — нет неопределённости.
Re[121]: Когда это наконец станет defined behavior?
От: σ  
Дата: 22.08.23 12:53
Оценка: -1
S>>>Так у нас вы тут за теоретика. Вот, доказываете, что если есть const demo d, то в конструкторе d мы будем иметь дело с const demo. А на практике так не получается.

σ>>Как можно сказать, равно ли A B, если из них ты знаешь только A или только B


S>Я правильно понимаю, что вы можете привести пример, который докажет, что в конструкторе const demo тип именно const demo, а не demo?


Доказывают, приводя доказательство

Но вообще ладно, вот: пример "2+2=4" доказывает, что в конструкторе const demo тип именно const demo.
Отредактировано 22.08.2023 12:55 σ . Предыдущая версия .
Re[111]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 12:53
Оценка:
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, vopl, Вы писали:


V>>что такое создание объекта (вот это есть определение создания объекта. И тут lifetime-а нет)


S>Не могу согласиться с тем, что фраза, перечисляющая способы создания объектов (by definition, by new-expression и т.д.), есть определение такого понятия как "создание объекта".


но это так

S>Тем более, что создание объекта в качестве временного объекта и посредством new-expression -- это разный набор действий (т.е. создание объекта в каждом из способов будет означать несколько разный набор операций).

вот тут наверное немного недопонимание есть: надо различать создание, конструирование, лайфтайм и т.д. Созданный объект не факт что можно использовать как обычно, он может быть еще непроинициализирован, его лайфтайм может еще не начаться. Другими словами: объекта в качестве временного объекта или объект, порожденный new-expression — они претерпевают не только "создание". Там масса всего интересного происходит
— выделяется соответствующий storage
— создается объект
— инициализируется
— стартует его lifetime

и только после всей этой пачки объект отдается на вызывающий уровень. Обрати внимание, тут "создание" — это один из шагов. И он не объемлет остальные, но стоит с ними на одном уровне. Фактически, "создание объекта" — это просто условность, некая реперная точка, после которой объект обретает свои свойства (имя, тип и прочие). Вот такую реперную точку называют "созданием объекта". Такая реперная точка "создание", как один из шагов проводится при определении объекта, в new-expression, а так же, просто как некая неявная штука — называется implicit creating. То есть, implicit creation — это такой creаting, только "неявный". Например, std::memcpy — делает такой implicit creating, а следовательно, после std::memcpy у объекта появляются имя, тип и прочее, далее его можно тем или иным способом проинициализировать, стартануть liifetime, далее с ним можно делать регулярную работу...

S>Т.е. эта фраза перечисляет легальные способы возникновения объектов в программе. Типа если объект появился в результате одного действия из перечисленных, то он считается созданным.


ну да

S>Собственно, как раз то, что раньше объекты могли возникать в программе нелегально с точки зрения стандарта, и пришлось вводить такие вещи, как implicit lifetime types, implicitly creates в купе с std::start_lifetime_as и т.д.


ага

S>Опять же это все к тому, что стандарт написан специальным языком для специалистов и для специфических целей.


ну вот мы с тобой сейчас читаем понемножку (ты же читаешь, да? Ведь не веришь же мне на слово?) и все становится понятно. Значит мы с тобой специалисты И у нас специальный цели
Re[104]: Когда это наконец станет defined behavior?
От: B0FEE664  
Дата: 22.08.23 12:55
Оценка:
Здравствуйте, σ, Вы писали:

σ>Короче. (При NRVO) возможны только две вещи, если ты хочешь прицепиться к лайфтайму и инициализации

σ>1. Если лайфтайм начинается после первой инициализации, то объект константный уже в функции и его модифицировать — UB
σ>2. Если после последней, то объект вне lifetime на протяжении выполнения всей функции, и его модифицировать — UB
Это ложная альтернатива. Вот цитата:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object, even if the constructor selected for the copy/move operation and/or the destructor for the object
have side effects. In such cases, the implementation treats the source and target of the omitted copy/move
operation as simply two different ways of referring to the same object.

Тут говорится, что реализация работает двумя различными путями с двумя объектами как с одним и тем же объектом. Здесь не утверждается, что объект один. Объектов два: "source and target". А работают с ними, как с одним и тем же объектом опуская операции копирования и перемещения. То, что эти операции опускаются, не означает, что объект один — формально объектов два, но работают с ними, как с одним объектом. Из того, что операцию копирования пропустили не следует, что начало времени жизни объекта куда-то перенеслось. Начало время жизни объекта осталось там же, где и было. И это относится к обоим объектам.

std::map<int, int> buildMap()
{
    std::map<int, int> result; // создали неконстантный объект result, после выполнения конструктора началось время жизни объекта result
    result.insert(42, 43);     // изменили неконстантный объект result
    return result;             // скопировали неконстантный объект result в возвращаемый неконстантный объект (*) - это операция пропускается
                               //   после завершения конструктора временного объекта началось время жизни возвращаемого неконстантного объекта несмотря на то, что операция копирования была пропущена.
                               // перед вызовом деструктора объекта result заканчивается время жизни объекта result
                               // разрушили неконстантный объект result (*) - это операция пропускается
}

const std::map<int, int> someDictionary = buildMap(); // возвращаемый функцией buildMap() неконстантный объект передаётся в конструктор класса map             (*) - это операция пропускается 
                                                      // внутри конструктора возвращаемый функцией buildMap() неконстантный объект перемещается (копируется)   (*) - это операция пропускается
                                                      // после завершения выполнения конструктора                                                              (*) - это операция пропускается 
                                                      //   началось время жизни константного объекта обозначаемого переменной someDictionary 
                                                      // время жизни возвращаемого функцией buildMap() неконстантного объекта окончено  
                                                      // после выполнения выражения возвращаемый функцией buildMap() неконстантный объект разрушается          (*) - это операция пропускается

— вот так (без подробностей) может выглядеть выполнение на абстрактной машине. Как видите время жизни константного объекта обозначаемого переменной someDictionary начинается после создания и модификации объекта.


σ>>>Раз время жизни объекта не началось, то result.insert(42, 43); — вызов метода на объекте до начала времени жизни? Тогда получается UB есть.

BFE>>Разумеется это не так. Объект может изменятся внутри конструктора
σ>И к чему ты это?
К тому, что методы можно вызывать до начала времени жизни.

σ> По-твоему, std::map<int, int> buildMap() это конструктор?

нет.

BFE>>Вы ошибаетесь и ваш авторитет ошибается. Я же вас с самого начала предупреждал, что попытка описания на естественном языке формального поведения однозначно приведёт к проблемам толкования.

σ>Если я не дал релевантных ссылок, это ещё не делает его описанием на естественном языке.
Ссылки на неформальный язык не делают описание формальным.

σ> То, что я описал, вполне формально-буквально следует из стандарта.

Формально-буквально из стандарта вообще мало чего следует.

BFE>>И ваше описание формально не корректно. Если формально, то константный объект не может становится неконстантным: A const object is an object of type const T.

σ>И как из этой цитаты следует, может или не может?
Неконстантный объект не будет удовлетворять определению константного объекта.

σ>Интересно самому найти формальное описание? Или мне привести?

σ>Могу пока дать поцсказку. Ты ведь, надеюсь, в курсе, чем создание объекта отличается от начала его времени жизни? Ну так вот. Ты, похоже, сразу перескочил на описание начала времени жизни, и зря. Почитай внимательно про создание объекта
Приведите, но имейте ввиду, что до начала жизни константный объект можно менять. Надеюсь вам понятно, что, например, во время выполнения конструктора константный объект остаётся константным объектом несмотря на то, что изменяется?

σ>Я описываю формальное описание.

Формальное описание может быть только на формальном языке.
И каждый день — без права на ошибку...
Re[107]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 12:58
Оценка:
Здравствуйте, vdimas, Вы писали:

сдаюсь, ты победил
Re[112]: Когда это наконец станет defined behavior?
От: so5team https://stiffstream.com
Дата: 22.08.23 13:04
Оценка: +1
Здравствуйте, vopl, Вы писали:

V>вот тут наверное немного недопонимание есть: надо различать создание, конструирование, лайфтайм и т.д. Созданный объект не факт что можно использовать как обычно, он может быть еще непроинициализирован, его лайфтайм может еще не начаться. Другими словами: объекта в качестве временного объекта или объект, порожденный new-expression — они претерпевают не только "создание". Там масса всего интересного происходит

V>- выделяется соответствующий storage
V>- создается объект
V>- инициализируется
V>- стартует его lifetime

Здесь понятно все, кроме "создается объект".
Как по мне, так создание -- это как раз выделение хранилища и, если возможно, инициализация. После того, как объект создан, его lifetime считается начатым. Хотя с lifetime, емнип, не все так просто, т.е. lifetime может начаться, но не считаться полностью начатым.

V>ну вот мы с тобой сейчас читаем понемножку (ты же читаешь, да? Ведь не веришь же мне на слово?)


Отдельные фрагменты.

V>и все становится понятно.


Не сказал бы.

Но вообще печально, что такими вещами приходится заниматься. Да не буду лишний раз повторять банальности.

V>И у нас специальный цели


Или так: нет цели, но есть путь

Re[105]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 13:08
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, σ, Вы писали:


σ>>Короче. (При NRVO) возможны только две вещи, если ты хочешь прицепиться к лайфтайму и инициализации

σ>>1. Если лайфтайм начинается после первой инициализации, то объект константный уже в функции и его модифицировать — UB
σ>>2. Если после последней, то объект вне lifetime на протяжении выполнения всей функции, и его модифицировать — UB
BFE>Это ложная альтернатива. Вот цитата:
BFE>

BFE>When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
BFE>object, even if the constructor selected for the copy/move operation and/or the destructor for the object
BFE>have side effects. In such cases, the implementation treats the source and target of the omitted copy/move
BFE>operation as simply two different ways of referring to the same object.

BFE>Тут говорится, что реализация работает двумя различными путями с двумя объектами как с одним и тем же объектом. Здесь не утверждается, что объект один. Объектов два: "source and target".

source and target это не объекты, это "two different ways of referring to the same object"
Re[113]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 13:16
Оценка:
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, vopl, Вы писали:


V>>вот тут наверное немного недопонимание есть: надо различать создание, конструирование, лайфтайм и т.д. Созданный объект не факт что можно использовать как обычно, он может быть еще непроинициализирован, его лайфтайм может еще не начаться. Другими словами: объекта в качестве временного объекта или объект, порожденный new-expression — они претерпевают не только "создание". Там масса всего интересного происходит

V>>- выделяется соответствующий storage
V>>- создается объект
V>>- инициализируется
V>>- стартует его lifetime

S>Здесь понятно все, кроме "создается объект".

S>Как по мне, так создание -- это как раз выделение хранилища и, если возможно, инициализация.

нене, выделение хрнилища, инициализация и прочее — это не создание. Создание это просто такая точка, в которой объект формально обретает свои свойства (имя, тип, storage duration ...)

S>После того, как объект создан, его lifetime считается начатым. Хотя с lifetime, емнип, не все так просто, т.е. lifetime может начаться, но не считаться полностью начатым.

после того, как объект создан — ему можно сделать инициализацию/конструирование, после окончания которых начнется его lifetime. А можно и не сделать, тогда у нас будет объект вне lifetime и его нельзя использовать обычными способами. А может это будет спец-объект с implicit lifetime, тогда он после создания сразу может быть использован обычным способом.
Re[104]: Когда это наконец станет defined behavior?
От: B0FEE664  
Дата: 22.08.23 13:18
Оценка: +1
Здравствуйте, vopl, Вы писали:

BFE>>Здесь не говорится, что объект один и тот же. Здесь говорится, что с этими объектами работают, как с одним объектом опуская некоторые операции. Нигде не сказано, что пропуск операции — это тоже самое, что её отсутствие.


V>Вот это поворот .. Какая разница, "он один и тот же" или "действия с ними трактуются как с одним и тем же".

Формально разница есть. UB привязано ко времени жизни. Время жизни привязано к концу/началу выполнения конструктора/деструктора. Конц/начало выполнения конструктора/деструктора привязано к последовательностям выполнения выражений. Если выполнение выражения пропускается, то вот "точки привязки" времени жизни остаются. Поэтому ссылаемся мы на один и тот же объект, но делаем вид, что это разные объекты. Делаем вид — это рассматриваем выражения так, как если бы это были разные объекты.
И каждый день — без права на ошибку...
Re[105]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 13:40
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>...но делаем вид, что это разные объекты. Делаем вид — это рассматриваем...


так нельзя, все эти "делаем вид" должны иметь обоснование, а его нет. Поэтому нет никаких "делаем вид". Выдавлен copy — значит его эффект стал пустым. Весь эффект целиком. Нет оснований полагать, что copy elision относится только к части copy. Он относится ко всему copy целиком, со всеми его эффектами, включая связность с lifetime
Re[106]: Когда это наконец станет defined behavior?
От: B0FEE664  
Дата: 22.08.23 13:41
Оценка:
Здравствуйте, vopl, Вы писали:

BFE>>

BFE>>When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
BFE>>object, even if the constructor selected for the copy/move operation and/or the destructor for the object
BFE>>have side effects. In such cases, the implementation treats the source and target of the omitted copy/move
BFE>>operation as simply two different ways of referring to the same object.

BFE>>Тут говорится, что реализация работает двумя различными путями с двумя объектами как с одним и тем же объектом. Здесь не утверждается, что объект один. Объектов два: "source and target".

V>source and target это не объекты, это "two different ways of referring to the same object"

А я уже писал выше по ветке: "Последнее время ссылаться на стандарт становится дурным тоном, так как комитет занят безумной попыткой описать формальное поведение на естественном английском языке. Эта задача не выполнима, так как под каждое словесное описание придётся дополнительно писать толкование написанных слов."
Ваша трактовка — иллюстрация моих слов.

Мой перевод такой:
В этих случаях реализация рассматривает источник и цель пропущенных операций копирования/перемещения как просто два разных способа сослаться на один и тот же объект.
Согласен, в общем случае можно подумать, что источник и цель — не объекты, если не учитывать контекст предыдущего предложения.
И каждый день — без права на ошибку...
Re[106]: Когда это наконец станет defined behavior?
От: B0FEE664  
Дата: 22.08.23 13:43
Оценка:
Здравствуйте, vopl, Вы писали:

V> Он относится ко всему copy целиком, со всеми его эффектами, включая связность с lifetime


Ну попробуйте это обосновать.
И каждый день — без права на ошибку...
Re[107]: Когда это наконец станет defined behavior?
От: vopl Россия  
Дата: 22.08.23 13:55
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, vopl, Вы писали:


V>> Он относится ко всему copy целиком, со всеми его эффектами, включая связность с lifetime


BFE> Ну попробуйте это обосновать.


Ну, обоснование у меня вряд ли получится сформулировать. Вместо обоснования — приведу свою логику рассуждений:
нет оснований полагать что в стандарте под выдавливанием copy пониматся не copy а некий "copy part", так как там не указано, какие именно аспекты copy будут выдавлены а какие — нет. Таким образом я считаю что имеется ввиду весь copy целиком.

Возможно, моя логика ошибочна, но пока что я придерживаюсь именно ее.
Отредактировано 22.08.2023 13:57 vopl . Предыдущая версия .
Re[105]: Когда это наконец станет defined behavior?
От: σ  
Дата: 22.08.23 14:27
Оценка:
σ>>Короче. (При NRVO) возможны только две вещи, если ты хочешь прицепиться к лайфтайму и инициализации
σ>>1. Если лайфтайм начинается после первой инициализации, то объект константный уже в функции и его модифицировать — UB
σ>>2. Если после последней, то объект вне lifetime на протяжении выполнения всей функции, и его модифицировать — UB
BFE>Это ложная альтернатива. Вот цитата:
BFE>

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object, even if the constructor selected for the copy/move operation and/or the destructor for the object
have side effects. In such cases, the implementation treats the source and target of the omitted copy/move
operation as simply two different ways of referring to the same object.

BFE>Тут говорится, что реализация работает двумя различными путями с двумя объектами как с одним и тем же объектом. Здесь не утверждается, что объект один. Объектов два: "source and target".

С чего ты решил, что source and target это объекты, а не (glvalue-)выражения или переменные?
Дальше про source и target написано, что это «ways of referring to the same object».
Что, скорее, является way of referring to an object: переменная/выражение или объект? Переменную или выражение вполне можно обозвать "способом ссылаться на объект". А называть объект "способом ссылаться на объект"? Ну ХЗ…

BFE> То, что эти операции опускаются, не означает, что объект один — формально объектов два


Формально объект один, формально по-прежнему два способа на него ссылаться. Которые теперь ссылаются на один и тот же объект.

BFE>
std::map<int, int> buildMap()
{
    std::map<int, int> result; // создали неконстантный объект result, после выполнения конструктора началось время жизни объекта result
    result.insert(42, 43);     // изменили неконстантный объект result
    return result;             // скопировали неконстантный объект result в возвращаемый неконстантный объект (*) - это операция пропускается
                               //   после завершения конструктора временного объекта началось время жизни возвращаемого неконстантного объекта несмотря на то, что операция копирования была пропущена.
                               // перед вызовом деструктора объекта result заканчивается время жизни объекта result
                               // разрушили неконстантный объект result (*) - это операция пропускается
}
const std::map<int, int> someDictionary = buildMap(); // возвращаемый функцией buildMap() неконстантный объект передаётся в конструктор класса map             (*) - это операция пропускается 
                                                      // внутри конструктора возвращаемый функцией buildMap() неконстантный объект перемещается (копируется)   (*) - это операция пропускается
                                                      // после завершения выполнения конструктора                                                              (*) - это операция пропускается 
                                                      //   началось время жизни константного объекта обозначаемого переменной someDictionary 
                                                      // время жизни возвращаемого функцией buildMap() неконстантного объекта окончено  
                                                      // после выполнения выражения возвращаемый функцией buildMap() неконстантный объект разрушается          (*) - это операция пропускается

BFE>- вот так (без подробностей) может выглядеть выполнение на абстрактной машине. Как видите время жизни константного объекта обозначаемого переменной someDictionary начинается после создания и модификации объекта.

Ну ок, теперь я. Буду идти по sequenced before control flow-а. (Подразумевается C++17+, так что временных объектов нет в принципе, а не потому что copy elision)

Встречаем definition const std::map<int, int> someDictionary ….
Стандарт говорит в [intro.object]/1 «An object is created by a definition» и «The properties of an object are determined when the object is created», а в [basic.type.qualifier]/1 «The type of an object includes the cv-qualifiers specified in the decl-specifier-seq… when the object is created».
decl-specifier-seq здесь это const std::map<int, int>, так что тип объекта const std::map<int, int> (ну, по крайней мере, тип точно включает в себя const, согласно цитате).

Дальше вызывается функция buildMap, где встречаем ещё один definition: std::map<int, int> result. Как описано выше, создаётся объект и назначаются его свойства, в частности, тип std::map<int, int>.
Когда у нас NRVO, мы второй раз определяем ему свойства одного и того же объекта, убрая const-квалификатор.

Дальше надо решать, после которой инициализации мы начинаем лайфтайм. Допустим, после первой (закончившейся), для result.

Ну а дальше объект так и остаётся с типом без const-квалификатора, т.к. его properties больше не determine ¯\_(ツ)_/¯

σ>>>>Раз время жизни объекта не началось, то result.insert(42, 43); — вызов метода на объекте до начала времени жизни? Тогда получается UB есть.

BFE>>>Разумеется это не так. Объект может изменятся внутри конструктора
σ>>И к чему ты это?
BFE>К тому, что методы можно вызывать до начала времени жизни.
During construction/destruction, так что, к чему это здесь?

σ>> По-твоему, std::map<int, int> buildMap() это конструктор?

BFE>нет.
Тогда, опять же, непонятно, зачем инфа про то, что можно до начала лайфтайма вызывать.

σ>> То, что я описал, вполне формально-буквально следует из стандарта.

BFE>Формально-буквально из стандарта вообще мало чего следует.
Конкретно в данном случае можно наскрести вполне однозначных формулировок.

BFE>>>И ваше описание формально не корректно. Если формально, то константный объект не может становится неконстантным: A const object is an object of type const T.

σ>>И как из этой цитаты следует, может или не может?
BFE>Неконстантный объект не будет удовлетворять определению константного объекта.
Ну у нас после std::map<int, int> result и получается неконстантный объект. Который таким и остаётся, даже если на него ссылаться через someDictionary, после возврата из buildMap
Отредактировано 22.08.2023 14:37 σ . Предыдущая версия .
Re[100]: Когда это наконец станет defined behavior?
От: rg45 СССР  
Дата: 22.08.23 15:01
Оценка: +1
Здравствуйте, vopl, Вы писали:

V>аа.. я ориентировался на это
Автор: σ
Дата: 16.08.23


V>Выше приводил конкретные 5 пунктов, даже с косвенной отсылкой на стандарт, посредством которых мною допускается гипотеза сабжевого UB. То есть, те 5 пунктов объясняют, каким образом допускается гипотеза про UB. Я не просил никаких ссылок на стандарт, наоборот, готов предоставить их явно для обоснования тех или иных своих тезисов. На счет того что "мне кажется" — тут да, мне действительно кажется что здесь может быть оттрактовано UB, и приведенные выше 5 пунктов объясняют каким именно образом.


Смотри, какая штука получается, давай еще раз посмотрим на этот абзац в стандарте:

https://timsong-cpp.github.io/cppwp/class.copy.elision#1

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object. If the first parameter of the selected constructor is an rvalue reference to the object's type, the destruction of that object occurs when the target would have been destroyed; otherwise, the destruction occurs at the later of the times when the two objects would have been destroyed without the optimization.


Object-то the same, но вот ways are different и это ключевой момент, который покрывает сразу и разницу времен жизни, и разницу в константности. А еще хочу заострить внимание на вот этой фразе: "an implementation is allowed...". То есть, не говорится, что компилятор обязан так сделать — ему это разрешается. И как именно он поступит, так или эдак, программист даже знать не обязан. В итоге программист имеет полное право считать, что в данном случае создаются два (или даже три) объекта, с разными временами жизни и разной константностью. И правильно тут рядом сказали: NRVO — это разновидность оптимизации, как ни крути.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 22.08.2023 15:02 rg45 . Предыдущая версия .
Re[108]: Когда это наконец станет defined behavior?
От: B0FEE664  
Дата: 22.08.23 15:20
Оценка: :)
Здравствуйте, vopl, Вы писали:

V>>> Он относится ко всему copy целиком, со всеми его эффектами, включая связность с lifetime

BFE>> Ну попробуйте это обосновать.
V>Ну, обоснование у меня вряд ли получится сформулировать. Вместо обоснования — приведу свою логику рассуждений:
V>нет оснований полагать что в стандарте под выдавливанием copy пониматся не copy а некий "copy part", так как там не указано, какие именно аспекты copy будут выдавлены а какие — нет. Таким образом я считаю что имеется ввиду весь copy целиком.

lifetime — это не результат создания объекта. lifetime — это свойство объекта, присущее объекту в некоторые отрезки выполнения программы (от конца конструктора до начала деструктора).
Если это свойство выкидывается вместе с конструктором, то в коде примера ниже someDictionary вообще не начинает жить, а следовательно, const должен игнорироваться полностью, следовательно объект someDictionary можно менять (ведь его время жизни ещё не началось):
std::map<int, int> buildMap()
{
    std::map<int, int> result;
    return result;            
}

const std::map<int, int> someDictionary = buildMap();
someDictionary[42] = 43;


А это некоторый абсурд, так что выкидывать lifetime вместе с конструктором нельзя.
И каждый день — без права на ошибку...
Re[109]: Когда это наконец станет defined behavior?
От: σ  
Дата: 22.08.23 15:22
Оценка: :)
BFE>Если это свойство выкидывается вместе с конструктором, то в коде примера ниже someDictionary вообще не начинает жить, а следовательно, const должен игнорироваться полностью, следовательно объект someDictionary можно менять (ведь его время жизни ещё не началось)


https://timsong-cpp.github.io/cppwp/n4868/basic.life#7.sentence-4
https://timsong-cpp.github.io/cppwp/n4868/res.on.objects#2
Отредактировано 22.08.2023 15:26 σ . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.