#c #c 17
Вопрос:
Учитывая функцию, которая возвращает объект с большим объемом для создания и с большим объемом для уничтожения, есть ли способ пропустить создание и уничтожение объекта, если возвращаемое значение функции не используется?
HeavyObject func();
auto res = func(); // heavy object is constructed and returned
func(); // lightweight "null" object is returned and immediately destroyed
Есть ли техника, которую я могу использовать, кроме отправки тегов, например void func(NotConsumed);
?
Обновить
Можно отказаться от возвращаемого значения, поэтому [[nodiscard]]
это не вариант. Клиенты могут решить, хотят ли они использовать результат или нет.
Давайте предположим func()
, что он инициирует какую-то работу и HeavyObject
служит своего рода ручкой. Клиенты могут использовать его позже для контроля/мониторинга работы, если захотят. Создание такого дескриптора может включать, например, создание каналов или выделение памяти в куче.
Существуют и другие перегрузки func()
, при которых возвращаемое значение должно быть использовано и которые помечены как [[nodiscard]]
. Новая перегрузка не имеет этого требования, но должна быть совместима с существующим API.
Комментарии:
1. Прежде всего, копирование (и создание копий) может быть исключено. Во-вторых, вы всегда можете перемещать «тяжелые» предметы. А компиляторы хороши в оптимизации… Постройте с включенной оптимизацией и просмотрите сгенерированный код сборки, чтобы узнать, создан ли такой объект или нет.
2. Вы также можете использовать
[[nodiscard]]
атрибут для создания предупреждения, если тяжелый объект отброшен.3. Что еще
func()
можно сделать? Есть ли у этого какие-то другие побочные эффекты, а также создание тяжелого объекта? (т. Е. Я веду к вопросу «можете ли вы разделить функциональность?»)4. вы можете вернуть объект с ленивой конструкцией
5. Я бы сказал, что
func
это делает две вещи одновременно, потому что кажется, что он что-то делает, и, кроме того, он необязательно создает и возвращает объект, но ваш вопрос предполагает, что это две отдельные проблемы
Ответ №1:
Начиная с C 17, существует nodiscard
атрибут. Однако это поможет только не игнорировать возвращаемое значение, и компилятору рекомендуется только выдавать предупреждение.
Я не думаю, что есть простой ответ на то, что вы хотите (т. Е. Никакой второй функции или перегрузки). Также потому, что построение тяжелого объекта происходит уже внутри функции, а не после ее возврата.
Что вы можете сделать, так это вернуть прокси-сервер, который только при необходимости создает тяжелый объект. Что-то в этом роде :
struct heavy_object { int x; };
struct proxy {
int parameter_needed_to_construct_heavy_object;
operator heavy_object() {
return {parameter_needed_to_construct_heavy_object};
}
};
proxy func() {
return {42};
}
int main() {
func(); // ok, no heavy object is constructed
heavy_object ho = func(); // ok, heavy object is constructed
auto x = func(); // "ok-ish", caller must be aware that x is just the proxy
}
Вместо неявного преобразования a get()
может занять его место, но это не изменит того факта, что вызывающий должен сделать что-то дополнительное, чтобы получить фактическое возвращаемое значение.
Сказав это, я думаю, что использование двух отдельных функций или перегрузки, как вы предлагаете, является более простым и чистым решением.
Комментарии:
1. Я не уверен, но этот код, вероятно, уменьшит использование семантики перемещения, не так ли? потому что тяжелый объект будет сохранен в члене класса.
2. @Afshin Я не понимаю, что ты имеешь в виду. Нет
heavy_object
хранится как член. Возможноoperator heavy_object
, необходимо настроить, чтобы использовать оптимизацию значений повторного возврата, я не очень хорошо разбираюсь в этом, но, по крайней мере, я уверен, что это не сильно отличаетсяheavy_object
отfunc
прямого возврата