Russian Qt Forum
Ноябрь 22, 2024, 18:24 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Протаскивание большой структуры через череду return  (Прочитано 10295 раз)
Kurles
Бывалый
*****
Offline Offline

Сообщений: 480



Просмотр профиля
« : Июль 11, 2016, 17:21 »

Доброго времени суток.

Иногда нужно протащить тяжеловесную структуру через несколько return (тот же паттерн pimpl у Qt).

Синтетический пример:

Код
C++ (Qt)
#include <stdio.h>
 
struct SomeLargeStuct {
   char data[100000];
   // ....
};
 
SomeLargeStuct f1 () {
   SomeLargeStuct s;
   s.data[0] = 'a';
   s.data[1] = 0x0a;
   s.data[2] = 0x00;
   return s;
}
 
SomeLargeStuct f2() {
   return f1();
}
 
SomeLargeStuct f3() {
   return f2();
}
 
 
int main(int argc, char *argv[])
{
   (void)argc;
   (void)argv;
   auto r = f3();
   printf(r.data);
   return 0;
}
 

Задумался над накладными расходами, он же по идее должен каждый раз в новой функции через конструктор копирования новый объект создавать? Добавил для теста в SomeLargeStuct отладочный вывод как в конструктор копирования так и в обычный конструктор - судя по выводу обычный конструктор вызвался один раз, конструктор копирования же совсем не вызывался.

Собственно вопрос - я могу на данное поведение рассчитывать во всех подобных случаях, или компилятор этот синтетический пример так оптимизировал? Ну и если у кого есть что почитать на эту тему - буду премного благодарен.

Записан

Код
C++ (Qt)
while(!asleep()) sheep++;
kai666_73
Крякер
****
Offline Offline

Сообщений: 319


Просмотр профиля
« Ответ #1 : Июль 11, 2016, 17:32 »

Почитайте о семантике перемещения в новом стандарте крестов, реализуйте ее для своей структуры  Подмигивающий
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #2 : Июль 11, 2016, 17:41 »

Вместо того, чтобы сделать по-нормальному и передавать в функцию неконстантную ссылку на возвращаемое значение (как это умеет Паскаль с 70-х годов), язык дополнили очередным костылем Грустный 21 век, мля...
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #3 : Июль 11, 2016, 20:57 »

Вместо того, чтобы сделать по-нормальному и передавать в функцию неконстантную ссылку на возвращаемое значение (как это умеет Паскаль с 70-х годов), язык дополнили очередным костылем Грустный 21 век, мля...
По нормальному?  Строит глазки
А что в этом нормального? Это же костыль.
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #4 : Июль 11, 2016, 20:59 »

Задумался над накладными расходами, он же по идее должен каждый раз в новой функции через конструктор копирования новый объект создавать? Добавил для теста в SomeLargeStuct отладочный вывод как в конструктор копирования так и в обычный конструктор - судя по выводу обычный конструктор вызвался один раз, конструктор копирования же совсем не вызывался.

Собственно вопрос - я могу на данное поведение рассчитывать во всех подобных случаях, или компилятор этот синтетический пример так оптимизировал? Ну и если у кого есть что почитать на эту тему - буду премного благодарен.

стандарт обязывает компиляторы поддерживать rvo/nrvo оптимизации.
более того, стандарт разрешает компиляторам
забить на любые возможные эффекты
в конструкторе копии,
что бы у последних было больше шансов на оптимизацию.
cм. cтандарт: 12.8/31 "copy elision"

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

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

лично я не стал бы закладываться на оптимизацию,
и передавал бы по ссылке:

Код:
#include <stdio.h>
 
struct SomeLargeStuct {
    char data[100000];
    // ....
};
 
void f1 (SomeLargeStuct& s) {
    s.data[0] = 'a';
    s.data[1] = 0x0a;
    s.data[2] = 0x00;
}
 
void f2(SomeLargeStuct& obj) {
    f1(obj);
}
 

// --- реализация находится в хедере
// такую функцию даже древние компиляторы
// смогут оптимизировать
inline SomeLargeStuct f3() {
    SomeLargeStuct obj;
    f2(obj);
    return obj;
}
 
 
int main(int argc, char *argv[])
{
    (void)argc;
    (void)argv;
    auto r = f3();
    printf(r.data);
    return 0;
}

« Последнее редактирование: Июль 11, 2016, 21:01 от _Bers » Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #5 : Июль 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, который может быть непосредственно модифицирован.

« Последнее редактирование: Июль 11, 2016, 21:43 от Racheengel » Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Kurles
Бывалый
*****
Offline Offline

Сообщений: 480



Просмотр профиля
« Ответ #6 : Июль 11, 2016, 21:55 »

стандарт обязывает компиляторы поддерживать rvo/nrvo оптимизации.
Спасибо, погуглил по выделенным ключевым словам - в моём примере оно и работает по-ходу.
Записан

Код
C++ (Qt)
while(!asleep()) sheep++;
kai666_73
Крякер
****
Offline Offline

Сообщений: 319


Просмотр профиля
« Ответ #7 : Июль 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, который может быть непосредственно модифицирован.


Не ну конешно это было бы здорово... только вот c/cpp и паскаль сильно разные вещи. Так, например, в паскале вызов ф-ии должен быть к чему-то приэссайнен, иначе код не скомпилируется. А вот в плюсах сие совершенно необязательно, и куда, в случае подвисшего вызова функции, девать возвращенный объект с его ссылкой и кто будет отвечать за его удаление?
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #8 : Июль 12, 2016, 01:54 »

дык, если функция подвисла, то она и так до возврата не дойдет Улыбающийся
а вообще - нет объекта - нет возврата.
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Июль 12, 2016, 08:47 »

Собственно вопрос - я могу на данное поведение рассчитывать во всех подобных случаях, или компилятор этот синтетический пример так оптимизировал? Ну и если у кого есть что почитать на эту тему - буду премного благодарен.
Это "почти поддерживается", но не всегда, не 100%. Напр MSVC в Debug вызовет все конструкторы. Также без объявления в левой части копирование состоится, напр
Код
C++ (Qt)
SomeLargeStuct s;
..
s = f1();   // здесь оптимизации не будет
 
Поэтому неконстантная ссылка или указатель надежнее
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Июль 12, 2016, 08:58 »

Не ну конешно это было бы здорово... только вот c/cpp и паскаль сильно разные вещи. Так, например, в паскале вызов ф-ии должен быть к чему-то приэссайнен, иначе код не скомпилируется.
Насколько я помню, начиная с Turbo-Pascal 4.0 (или 5.0) присвоение уже не было обязательным, можно было писать ReadKey() (а не ch:= ReadKey()). Ничего "здорового" в result не вижу, громоздко и неудобно

А вот в плюсах сие совершенно необязательно, и куда, в случае подвисшего вызова функции, девать возвращенный объект с его ссылкой и кто будет отвечать за его удаление?
Это личное дело компилятора, обычно (если не оптимизируется) возвращаемая структура на стеке и ее деструктор (если есть) вызовется так же как для структуры объявленной в ф-ции (локальной)
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #11 : Июль 12, 2016, 09:12 »

Костыль - это как раз Move semantics, вынуждающая вводить "еще один" тип конструктора.
Костыль это передача не константной ссылки на объект. Который еще нужно сконструировать перед этим. А не все классы обязаны иметь конструктор по умолчанию.

А передавать большие структуры можно было уже давно. Посмотрите на "классы хранения" в Qt. А можно вернуть и shared_ptr на структуру.

Вместо этого достаточно было бы зарезервированного слова, по примеру this.
Что-то типа, например, result.
Вы сами возмущаетесь введением новых фич и тут же предлагаете свои. Вам нужно в комитет. Улыбающийся
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #12 : Июль 12, 2016, 12:01 »

Костыль это передача не константной ссылки на объект. Который еще нужно сконструировать перед этим. А не все классы обязаны иметь конструктор по умолчанию.

Если предполагается возвращать объект "по значению", то он в любом случае обязан иметь конструктор, явно или не явно.
Поэтому костыльности в этом никакой не вижу, а вот введение специальных конструкторов и есть самый настоящий костылище Улыбающийся

Думайте о неконстантной ссылке на результат так же, как и о параметре-ссылке. Разницы для компилятора не должно быть принципиально никакой.
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #13 : Июль 12, 2016, 12:39 »

Если предполагается возвращать объект "по значению", то он в любом случае обязан иметь конструктор, явно или не явно.
Ну так иметь конструктор != иметь конструктор по умолчанию.

Параметры для конструирования объекта могут быть доступны только в методе возвращаемом объект, но не раньше.

Код:
class Foo
{
public:
    // Нет конструктора по умолчанию!
    Foo( const string &id );
};

Foo Bar::foo()
{
    Foo result( m_id_manager->get_id() );
    return result;
}

Как вы планируете создать Foo там, где про m_id_manager ничего не известно и взять id просто негде.
Начать добавлять костыли в виде невалидного (временного) id?
Или вытащить менеджер id в глобальное пространство, где он не нужен?
И это все для того, что бы можно было его возвращать? Улыбающийся
« Последнее редактирование: Июль 12, 2016, 12:40 от Old » Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #14 : Июль 12, 2016, 12:53 »

Например, как то так:

Foo myFoo = Bar::foo(); // тут компилятор видит, что у нас возврат объекта по значению, и передает в метод ссылку на myFoo.

Foo Bar::foo()
{
    result.Foo( m_id_manager->get_id() ); // а тут мы вызываем конструктор для переданного myFoo
}

Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.363 секунд. Запросов: 23.