Название: Протаскивание большой структуры через череду return Отправлено: Kurles от Июль 11, 2016, 17:21 Доброго времени суток.
Иногда нужно протащить тяжеловесную структуру через несколько return (тот же паттерн pimpl у Qt). Синтетический пример: Код
Задумался над накладными расходами, он же по идее должен каждый раз в новой функции через конструктор копирования новый объект создавать? Добавил для теста в SomeLargeStuct отладочный вывод как в конструктор копирования так и в обычный конструктор - судя по выводу обычный конструктор вызвался один раз, конструктор копирования же совсем не вызывался. Собственно вопрос - я могу на данное поведение рассчитывать во всех подобных случаях, или компилятор этот синтетический пример так оптимизировал? Ну и если у кого есть что почитать на эту тему - буду премного благодарен. Название: Re: Протаскивание большой структуры через череду return Отправлено: kai666_73 от Июль 11, 2016, 17:32 Почитайте о семантике перемещения в новом стандарте крестов, реализуйте ее для своей структуры ;)
Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 11, 2016, 17:41 Вместо того, чтобы сделать по-нормальному и передавать в функцию неконстантную ссылку на возвращаемое значение (как это умеет Паскаль с 70-х годов), язык дополнили очередным костылем :( 21 век, мля...
Название: Re: Протаскивание большой структуры через череду return Отправлено: Old от Июль 11, 2016, 20:57 Вместо того, чтобы сделать по-нормальному и передавать в функцию неконстантную ссылку на возвращаемое значение (как это умеет Паскаль с 70-х годов), язык дополнили очередным костылем :( 21 век, мля... По нормальному? ::)А что в этом нормального? Это же костыль. Название: Re: Протаскивание большой структуры через череду return Отправлено: _Bers от Июль 11, 2016, 20:59 Задумался над накладными расходами, он же по идее должен каждый раз в новой функции через конструктор копирования новый объект создавать? Добавил для теста в SomeLargeStuct отладочный вывод как в конструктор копирования так и в обычный конструктор - судя по выводу обычный конструктор вызвался один раз, конструктор копирования же совсем не вызывался. Собственно вопрос - я могу на данное поведение рассчитывать во всех подобных случаях, или компилятор этот синтетический пример так оптимизировал? Ну и если у кого есть что почитать на эту тему - буду премного благодарен. стандарт обязывает компиляторы поддерживать rvo/nrvo оптимизации. более того, стандарт разрешает компиляторам забить на любые возможные эффекты в конструкторе копии, что бы у последних было больше шансов на оптимизацию. cм. cтандарт: 12.8/31 "copy elision" однако стандарт не гарантирует, что оптимизация будет возможна во всех случаях. то есть допускается, что в каких то случаях оптимизация и не сработает. на практике, компилятор может не суметь оптимизировать, если ему не доступен контекст использования. например, в хедере объявлена функция, которая возвращает по значению, но её реализация компилятору не доступна (допустим, реализация находится уже в собранной либке) лично я не стал бы закладываться на оптимизацию, и передавал бы по ссылке: Код: #include <stdio.h> Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 11, 2016, 21:39 А что в этом нормального? Это же костыль. Костыль - это как раз Move semantics, вынуждающая вводить "еще один" тип конструктора. Вместо этого достаточно было бы зарезервированного слова, по примеру this. Что-то типа, например, result. Таким образом, пример выше мог бы выглядеть как SomeLargeStuct f1 () { // SomeLargeStuct s; // это уже не надо. result.data[0] = 'a'; result.data[1] = 0x0a; result.data[2] = 0x00; //return s; // и это тоже в лес } а вызов выглядел бы в классическом виде: SomeLargeStuct s1 = f1(); Таким образом, в контекст f1 под алиасом result попадает ссылка на объект s1, который может быть непосредственно модифицирован. Название: Re: Протаскивание большой структуры через череду return Отправлено: Kurles от Июль 11, 2016, 21:55 стандарт обязывает компиляторы поддерживать rvo/nrvo оптимизации. Спасибо, погуглил по выделенным ключевым словам - в моём примере оно и работает по-ходу.Название: Re: Протаскивание большой структуры через череду return Отправлено: kai666_73 от Июль 11, 2016, 23:31 А что в этом нормального? Это же костыль. Костыль - это как раз Move semantics, вынуждающая вводить "еще один" тип конструктора. Вместо этого достаточно было бы зарезервированного слова, по примеру this. Что-то типа, например, result. Таким образом, пример выше мог бы выглядеть как SomeLargeStuct f1 () { // SomeLargeStuct s; // это уже не надо. result.data[0] = 'a'; result.data[1] = 0x0a; result.data[2] = 0x00; //return s; // и это тоже в лес } а вызов выглядел бы в классическом виде: SomeLargeStuct s1 = f1(); Таким образом, в контекст f1 под алиасом result попадает ссылка на объект s1, который может быть непосредственно модифицирован. Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 12, 2016, 01:54 дык, если функция подвисла, то она и так до возврата не дойдет :)
а вообще - нет объекта - нет возврата. Название: Re: Протаскивание большой структуры через череду return Отправлено: Igors от Июль 12, 2016, 08:47 Собственно вопрос - я могу на данное поведение рассчитывать во всех подобных случаях, или компилятор этот синтетический пример так оптимизировал? Ну и если у кого есть что почитать на эту тему - буду премного благодарен. Это "почти поддерживается", но не всегда, не 100%. Напр MSVC в Debug вызовет все конструкторы. Также без объявления в левой части копирование состоится, напрКод Поэтому неконстантная ссылка или указатель надежнее Название: Re: Протаскивание большой структуры через череду return Отправлено: Igors от Июль 12, 2016, 08:58 Не ну конешно это было бы здорово... только вот c/cpp и паскаль сильно разные вещи. Так, например, в паскале вызов ф-ии должен быть к чему-то приэссайнен, иначе код не скомпилируется. Насколько я помню, начиная с Turbo-Pascal 4.0 (или 5.0) присвоение уже не было обязательным, можно было писать ReadKey() (а не ch:= ReadKey()). Ничего "здорового" в result не вижу, громоздко и неудобноА вот в плюсах сие совершенно необязательно, и куда, в случае подвисшего вызова функции, девать возвращенный объект с его ссылкой и кто будет отвечать за его удаление? Это личное дело компилятора, обычно (если не оптимизируется) возвращаемая структура на стеке и ее деструктор (если есть) вызовется так же как для структуры объявленной в ф-ции (локальной)Название: Re: Протаскивание большой структуры через череду return Отправлено: Old от Июль 12, 2016, 09:12 Костыль - это как раз Move semantics, вынуждающая вводить "еще один" тип конструктора. Костыль это передача не константной ссылки на объект. Который еще нужно сконструировать перед этим. А не все классы обязаны иметь конструктор по умолчанию.А передавать большие структуры можно было уже давно. Посмотрите на "классы хранения" в Qt. А можно вернуть и shared_ptr на структуру. Вместо этого достаточно было бы зарезервированного слова, по примеру this. Вы сами возмущаетесь введением новых фич и тут же предлагаете свои. Вам нужно в комитет. :)Что-то типа, например, result. Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 12, 2016, 12:01 Костыль это передача не константной ссылки на объект. Который еще нужно сконструировать перед этим. А не все классы обязаны иметь конструктор по умолчанию. Если предполагается возвращать объект "по значению", то он в любом случае обязан иметь конструктор, явно или не явно. Поэтому костыльности в этом никакой не вижу, а вот введение специальных конструкторов и есть самый настоящий костылище :) Думайте о неконстантной ссылке на результат так же, как и о параметре-ссылке. Разницы для компилятора не должно быть принципиально никакой. Название: Re: Протаскивание большой структуры через череду return Отправлено: Old от Июль 12, 2016, 12:39 Если предполагается возвращать объект "по значению", то он в любом случае обязан иметь конструктор, явно или не явно. Ну так иметь конструктор != иметь конструктор по умолчанию.Параметры для конструирования объекта могут быть доступны только в методе возвращаемом объект, но не раньше. Код: class Foo Как вы планируете создать Foo там, где про m_id_manager ничего не известно и взять id просто негде. Начать добавлять костыли в виде невалидного (временного) id? Или вытащить менеджер id в глобальное пространство, где он не нужен? И это все для того, что бы можно было его возвращать? :) Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 12, 2016, 12:53 Например, как то так:
Foo myFoo = Bar::foo(); // тут компилятор видит, что у нас возврат объекта по значению, и передает в метод ссылку на myFoo. Foo Bar::foo() { result.Foo( m_id_manager->get_id() ); // а тут мы вызываем конструктор для переданного myFoo } Название: Re: Протаскивание большой структуры через череду return Отправлено: Old от Июль 12, 2016, 12:57 Например, как то так: Это ваша идея, сейчас так нельзя. :)Я лишь не разделяю мнение, что возврат структуры по не константной ссылке хорошее решение. :) Хотя, сам иногда его использую. Я бы сказал, что это просто, но не хорошо. :) Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 12, 2016, 13:07 Конечно, но это идея не моя, а товарища Вирта и компании, которые придумали Паскаль в 1970 ;)
И, честно говоря, я не услышал, почему это хуже, чем еще один типа конструктора ??? Offtop: Я слышал несколько раз мнения людей, которые довольно глубоко связаны с C++ и новым стандартов, по поводу новшеств - и они говорят, что язык просто искуственно подготавливают к вымиранию, т.к. в итоге он превратится в неподдерживаемую кашу и возникнет необходимость более гибкой и прозрачной, но в то же время производительной альтернативы. Название: Re: Протаскивание большой структуры через череду return Отправлено: Old от Июль 12, 2016, 13:17 И, честно говоря, я не услышал, почему это хуже, чем еще один типа конструктора ??? Если вы про свою идею, то я пока не очень ее обдумывал, что бы сформировалось мнение. Если вы про возврат через ссылку, то я выше привел пример.Offtop: Я слышал несколько раз мнения людей, которые довольно глубоко связаны с C++ и новым стандартов, по поводу новшеств - и они говорят, что язык просто искуственно подготавливают к вымиранию, т.к. в итоге он превратится в неподдерживаемую кашу и возникнет необходимость более гибкой и прозрачной, но в то же время производительной альтернативы. Каждый имеет право на свое мнение. Сколько людей - столько и мнений. :)Я тоже не использую все, что появилось в новом стандарте. Что то у меня прижилось, что-то нет. Но я не считаю, что все что у меня не прижилось - фигня, которую нужно срочно из стандарта убирать. :) Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 12, 2016, 14:34 Если вы про возврат через ссылку, то я выше привел пример. А чем приведенный пример противоречит идее возврата через ссылку? Я привел вариант, как это могло бы быть использовано :) Название: Re: Протаскивание большой структуры через череду return Отправлено: Old от Июль 12, 2016, 14:48 А чем приведенный пример противоречит идее возврата через ссылку? Тем, что могут быть классы, объекты которых не удастся создать в вызывающей функции. ::)Я привел вариант, как это могло бы быть использовано :) Название: Re: Протаскивание большой структуры через череду return Отправлено: ssoft от Июль 13, 2016, 08:47 std::move семантика предназначена не только для оптимизации возврата значений из методов, для языка программирования она имеет больше фундаментальное значение, связанное с модельным понятием агрегации (это очень большая тема, поэтому не буду расписывать). Развитие семантики связано с объективными причинами ограниченности языка, каждое нововведение имеет теоретическое и практическое обоснование. Если разработчик считает, что оно ему не надо, то он может их и не использовать.
Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 13, 2016, 10:34 std::move семантика предназначена не только для оптимизации возврата значений из методов, для языка программирования она имеет больше фундаментальное значение, связанное с модельным понятием агрегации (это очень большая тема, поэтому не буду расписывать). Развитие семантики связано с объективными причинами ограниченности языка, каждое нововведение имеет теоретическое и практическое обоснование. Если разработчик считает, что оно ему не надо, то он может их и не использовать. Обосновать при желании можно все, даже goto :) Любой костыль можно объявить фичей и пропихнуть в стандарт, но костылем от этого оно быть не перестанет. Думайте проще - разработчику надо решить задачу с минимальными затратами в минимальный срок, причем другие разработчики должны понять потом, что же было сделано. Итого - чем проще и прозрачнее синтаксис - тем лучше. Sugar на то и sugar :) Название: Re: Протаскивание большой структуры через череду return Отправлено: Racheengel от Июль 13, 2016, 10:35 Тем, что могут быть классы, объекты которых не удастся создать в вызывающей функции. ::) Но я же, вроде, написал, как можно бы было это обойти :) |