Вот такое дело. Допустим у класса storage есть функция fetch_data, которая возвращает объекты типа data по запросу:
data storage::fetch_data(string const & request);
Пока что data возвращается по значению. Но в большинстве случаев оказывается, что можно было бы возвратить и ссылку на data, сохраненный в storage:
data const & storage::fetch_data(string const & request);
И это было бы гораздо эффективнее текущей версии, так как не надо делать копию data.
Но иногда storage не может найти данные по запросу, и он их создаёт. И в этом случае по ссылке возвратить data уже нельзя. Именно поэтому текущая версия fetch_data возвращает по значению. Вопрос: как так возвратить data, чтобы в в большинстве случаев не делать копирования, а делать только в тех редких случаях, когда приходится?
Здравствуйте, l33thaxor, Вы писали:
L>Вот такое дело. Допустим у класса storage есть функция fetch_data, которая возвращает объекты типа data по запросу: L>
L>Пока что data возвращается по значению. Но в большинстве случаев оказывается, что можно было бы возвратить и ссылку на data, сохраненный в storage: L>
L>И это было бы гораздо эффективнее текущей версии, так как не надо делать копию data.
L>Но иногда storage не может найти данные по запросу, и он их создаёт. И в этом случае по ссылке возвратить data уже нельзя. Именно поэтому текущая версия fetch_data возвращает по значению. Вопрос: как так возвратить data, чтобы в в большинстве случаев не делать копирования, а делать только в тех редких случаях, когда приходится?
shared_ptr подойдет?
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, l33thaxor, Вы писали:
L>Здравствуйте, Ops, Вы писали:
Ops>>shared_ptr подойдет?
L>Каким образом? В storage объекты хранятся не ввиде shared_ptr.
В этом случае, когда storage создает новый объект, его прийдется выделять на хипе, а хотелось бы обойтись стеком. Да и shared_ptr со своим deleter'ом выглядить как то тяжеловато тоже. Больше никаких нет вариантов?
Здравствуйте, l33thaxor, Вы писали:
L>Здравствуйте, Ops, Вы писали:
Ops>>Здравствуйте, l33thaxor, Вы писали:
L>>>Здравствуйте, Ops, Вы писали:
Ops>>>>shared_ptr подойдет?
L>>>Каким образом? В storage объекты хранятся не ввиде shared_ptr.
Ops>>http://www.boost.org/doc/libs/1_45_0/libs/smart_ptr/sp_techniques.html#static
L>В этом случае, когда storage создает новый объект, его прийдется выделять на хипе, а хотелось бы обойтись стеком. Да и shared_ptr со своим deleter'ом выглядить как то тяжеловато тоже. Больше никаких нет вариантов?
Ну так все зависит от того, насколько тяжелые объекты и как потом с ними работают. А вместо выделения в хипе можно использовать пул для этих объектов, или даже просто placement new — тут все от многопоточности зависит.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, l33thaxor, Вы писали:
L>В этом случае, когда storage создает новый объект, его прийдется выделять на хипе, а хотелось бы обойтись стеком.
Ну вообще-то вернуть хоть ссылку, хоть указатель на объект, созданный на стеке внутри функции, вне ее — это никуда не годится.
Вопрос — что значит "создает новый объект" ? Один раз вызвали, не нашел, создал. Второй раз — то же ? Первый созданный объект к этому времени должен еще жить? или же можно его память использовать для второго ? Иными словами, их много может быть или в любой момент только один ? Если один — можно банально глобальную переменную завести.
Здравствуйте, ariets, Вы писали:
A>Здравствуйте, l33thaxor, Вы писали:
A>Вопрос: как так возвратить data, чтобы в в большинстве случаев не делать копирования, а делать только в тех редких случаях, когда приходится?
A>
...
A> staticconst data emptyData;
A> return emtyData;
A>}
A>
у вас же переменная static, возвращайте всегда ссылку.
A>const data &fetch_data( const std::string &request )
A>{
A> if( is_data( request ) )
A> {
A> return get_data( request );
A> }
A> static const data emptyData;
A> return emtyData;
A>}
A>
Статический объект не подходит. В этом случае не будет работать даже простейшая последовательность двух вызовов fetch_data:
data const & d1 = fetch_data(r1);
data const & d2 = fetch_data(r2); // d1 is clobbered
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Вопрос — что значит "создает новый объект" ? Один раз вызвали, не нашел, создал. Второй раз — то же ? Первый созданный объект к этому времени должен еще жить? или же можно его память использовать для второго ?
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, l33thaxor, Вы писали:
__>Имеется возможность произвести move ? __>Возможно это может будет эффективней всего.
__>С++0х ждать для move не обязательно, можно его реализовать вручную как в std::auto_ptr делается.
Непонятно, как мне это поможет. Мне нужно оптимизировать для случая возврата существующего объекта по ссылке. Это fast path. Возврат по значению, когда происходит создание нового объекта и копирование его при возврате из функции, это slow path. Она происходит редко и его оптимизировать не надо.
Здравствуйте, l33thaxor, Вы писали:
L>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Вопрос — что значит "создает новый объект" ? Один раз вызвали, не нашел, создал. Второй раз — то же ? Первый созданный объект к этому времени должен еще жить? или же можно его память использовать для второго ?
L>Должен жить. Память переиспользовать нельзя.
Тогда я не понимаю, как ты хочешь вернуть данные, расположенные на стеке. Допустим на минуту, что это возможно. Сделать-то как ? Нельзя же описать неопределенное число автоматических переменных.
Здравствуйте, Pavel Dvorkin, Вы писали:
L>>Должен жить. Память переиспользовать нельзя.
PD>Тогда я не понимаю, как ты хочешь вернуть данные, расположенные на стеке. Допустим на минуту, что это возможно. Сделать-то как ? Нельзя же описать неопределенное число автоматических переменных.
Я не хочу вернуть данные на стеке. Вопрос заключался в следующем: если данные уже существуют в storage, то вернуть их так же быстро как по сслыке, если же данных нет, то создать из и вернуть по значению.
На самом деле вот один вариант интерфейса, который удовлетворяет данному требованию. Я не хотел его сразу приводить, думал, что кто-нибудь приведет что-нибудь лучше.
// temp_obj - [in/out] указатель на временный объект, переданный из вызывающей стороны,
// в который будет положен объект data, если нельзя вернуть ссылку и необходимо создать новый объект
data const * storage::fetch_data(data * temp_obj, string const & request) {
if (data const * existing = retrieve_data(request)) {
return existing;
}
create_data(request).swap(*temp_obj);
return temp_obj;
}
// вызов
data temp_obj;
data const * fetched = storage.fetch_data(&temp_obj, request);
// работаем с fetched
// при выходе из scope, temp_obj освободит объект, который мог создать fetch_data
Здесь удовлетворены требования по производительности. Но мне этот интерфейс не нравится. Во-первых, вызов fetch_data немного кривой получается. А во-вторых, получается, что fetch_data имеет некоторый неоднозначный side effect.
Можно ли реализовать подобную идею с более приличным интерфейсом.
Здравствуйте, l33thaxor, Вы писали:
L>Здравствуйте, Pavel Dvorkin, Вы писали:
L>>>Должен жить. Память переиспользовать нельзя.
PD>>Тогда я не понимаю, как ты хочешь вернуть данные, расположенные на стеке. Допустим на минуту, что это возможно. Сделать-то как ? Нельзя же описать неопределенное число автоматических переменных.
L>Я не хочу вернуть данные на стеке. Вопрос заключался в следующем: если данные уже существуют в storage, то вернуть их так же быстро как по сслыке, если же данных нет, то создать из и вернуть по значению.
L>На самом деле вот один вариант интерфейса, который удовлетворяет данному требованию. Я не хотел его сразу приводить, думал, что кто-нибудь приведет что-нибудь лучше.
<skipped>
L>Здесь удовлетворены требования по производительности. Но мне этот интерфейс не нравится. Во-первых, вызов fetch_data немного кривой получается. А во-вторых, получается, что fetch_data имеет некоторый неоднозначный side effect.
L>Можно ли реализовать подобную идею с более приличным интерфейсом.
Честно говоря, мне все это совсем не нравится. Создается какой-то непонятный (и, возможно, ненужный) объект, потом внутри в него пересылают данные из вновь созданного, и все это ради того, чтобы этот объект потом мог сам удалиться на выходе из scope.
Я бы просто сделал. Вернул бы указатель то ли на найденный, то ли на прямо там созданный объект и завел бы еще один параметр, указывающий был ли объект создан или нет. Если нет — delete его и все дела.
data const * storage::fetch_data(string const & request, bool& wasFound) {
if (data const * existing = retrieve_data(request)) {
wasFound = true;
return existing;
}
wasFound = false
return create_data(request)
}
...
bool wasFound;
data const * result = fetch_data(request,wasFound);
// use resultif(!wasFound)
delete result;
А можно чуть похитрее
class data_and_bool
{
data* result;
bool wasFound;
~data_and_bool()
{
if(!wasFound)
delete data;
}
};
data_and_bool storage::fetch_data(string const & request) {
data_and_bool dab;
if (data const * existing = retrieve_data(request)) {
dab.wasFound = true;
dab.result = existing;
}
else {
dab.wasFound = false;
dab.result = create_data(request);
}
return dab;
}
data_and_bool dab = storage::fetch_data(request);
// и пусть деструктор уничтожает данные на выходе из scope, если они были созданы
}
Естественно, не компилировал, так что за возможные ошибки сорри. Считай это псевдокодом.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Я бы просто сделал. Вернул бы указатель то ли на найденный, то ли на прямо там созданный объект и завел бы еще один параметр, указывающий был ли объект создан или нет. Если нет — delete его и все дела.
Такая идея мне в голову приходила, но хотелось бы обойтись без создания объектов на хипе, если можно ограничиться стеком. В том интерфейсе, который я привел (с временным temp_obj), объекты создаются на стеке.
Если бы хип был приемлимым, то я бы выбрал вариант с shared_ptr, который был упомянут в первых ответах.
Здравствуйте, l33thaxor, Вы писали:
L>Такая идея мне в голову приходила, но хотелось бы обойтись без создания объектов на хипе, если можно ограничиться стеком. В том интерфейсе, который я привел (с временным temp_obj), объекты создаются на стеке.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, l33thaxor, Вы писали:
L>>Такая идея мне в голову приходила, но хотелось бы обойтись без создания объектов на хипе, если можно ограничиться стеком. В том интерфейсе, который я привел (с временным temp_obj), объекты создаются на стеке.
PD>Хм, а create_data где его создает ?