Проверьте, израсходовано ли возвращаемое значение

#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 прямого возврата