Если это не архимегакритический путь с точки зрения производительности, то надо сделать глобальную обертку а-ля OutParameter<vector<Something>>, которая сама будет занулять.
При условии, что каждый раз перед вызовом функции вектор создается заново, накладные расходы будут примерно, как при передаче ссылки на output.
Правда, тут есть одна неприятность, синтаксис
auto [res, error_code] = GetSomething();
поддерживают только gcc7 и последний MSVC (clang, скорее всего тоже, только вышедший или вот вот появится), в отличие от string_view, например, который есть уже везде в std::experimental.
result_code GetSomething(vector<Something>& output); ///! очистить буфер и положить в него новые значения
result_code AppendSomething(vector<Something>& output); ///! добавить в буфер новые значения (сохранив предыдущие)
Причем GetSomething() может просто сделать clear() и вызвать AppendSomething().
C++ описывает синтаксис, но никак не описывает семантику.
Так что из объявления функции никак не следует как ей можно пользоваться.
Например есть две функции void lock() и void unlock() то на вопрос "можно ли их вызывать влежено", "обязательно после lock вызывать unlock или нет" тишина.
Так что и у вас надо просто документировать что будет делать ваша функция.
Ну если это в твоем понимании примерно то, что ТС хотел, то ОК.
_>C++ описывает синтаксис, но никак не описывает семантику.
С++ вполне себе может в семантику из-за системы типов. Ты ему можешь сказать, что объекты одного типа можно только двигать, другого — только копировать например. Ты сам от всего этого (системы типов) отказался, зачем-то предложив наиболее общий, ничем не ограниченный шаблон. В этом случае действительно компилятор слепит функцию из всего синтаксически подходящего.
_>Так что из объявления функции никак не следует как ей можно пользоваться. _>Например есть две функции void lock() и void unlock() то на вопрос "можно ли их вызывать влежено", "обязательно после lock вызывать unlock или нет" тишина. _>Так что и у вас надо просто документировать что будет делать ваша функция.
Это ТС-ова функция, а не наша. Наша функция для начала имени в формате CamelCase иметь не будет .
B>Что скажете?
Если result_code имеет только два значения, то для этого придуман тип optional. Он давно есть в BOOST и появится в C++17 (cppreference). В VS2017 этот тип есть. Пример из cppreference:
#include <string>
#include <iostream>
#include <optional>
// optional can be used as the return type of a factory that may fail
std::optional<std::string> create(bool b) {
if(b)
return"Godzilla";
else
return {};
}
int main()
{
std::cout << "create(false) returned "
<< create(false).value_or("empty") << '\n';
// optional-returning factory functions are usable as conditions of while and ifif(auto str = create(true)) {
std::cout << "create(true) returned " << *str << '\n';
}
}
Здравствуйте, Basil2, Вы писали:
B>1. Всегда очищать (лишнее действие, что не в духе плюсов).
Неужели clear() для потенциально пустого вектора хоть как-то сопоставим по сложности с кодом, который будет потом этот вектор набирать или даже с самим вызовом функции (очевидно, что clear() заинлайнится и оптимизируется в какой-то cmp банальный)? Никто ведь не ожидает нулевого значения от х и у в случае функции
result_type get_xy(int& x, int& y);
чтобы потом вместо "x = 10", написать "x += 10". Так и в вашем случае — get есть get. Поэтому при такой сигнатуре — однозначно очищать.
B>2. Игнорировать, т.е. новые значения добавятся к старым ("не очистил сам дурак" — вполне в духе, но добавляет нестабильности).
Только в случае, если собираетесь явно обозначить это пользователю, назвав "append...". Однако в этом случае почему именно вектор? Тут уже лучше или функцию передавать или через открытый шаблон с итератором.
B>3. Должен сработать assert (типа assert(!output.empty)).
В самом крайнем случае, если это внутренности и очень важна оптимизация. Только, наверное "!" лишний?
B>4. Вернуть ошибку/бросить исключение.
Просто дичь.
B>При одном из вызовов функции передается непустой вектор. Как, на ваш взгляд, функция должна отреагировать на это?
Пару слов о принятых решениях:
1. Мне тоже нравится сигнатура vector<Something> GetSomething() (она еще и позволяет использовать константы), но по правилам используемой библиотеки все функции должны возвращать result_code.
2. В итоге принято решение "всегда очищать". Почему: потери времени минимальны (вектор ожидается пустым в абсолютном большинстве случаев). При этом возможные геморрои, связанные с отладкой или сложными названиями, полностью исключаются.
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
B>При одном из вызовов функции передается непустой вектор. Как, на ваш взгляд, функция должна отреагировать на это?
B>1. Всегда очищать (лишнее действие, что не в духе плюсов). B>2. Игнорировать, т.е. новые значения добавятся к старым ("не очистил сам дурак" — вполне в духе, но добавляет нестабильности). B>3. Должен сработать assert (типа assert(output.empty)). B>4. Вернуть ошибку/бросить исключение. B>5. Оставлю комментарий со своим вариантом.
B>(Хотел сделать настоящее голосование, но получаю "500 — Internal server error". Зато здесь можно обсудить более развернуто).
B>Что скажете?
ИМХО всё зависит от условий задачи/проекта.
Так, если мы только заполняем получаемыми значениями, то можно первоначально дать очистку вектора.
Если же GetSomething может что-то добавлять — то очистку делать не следует,
тогда логичнее назвать этот метод/функцию: Add... или Append...
Остальные варианты — представляются мне не актуальными для данного случая.
B>При одном из вызовов функции передается непустой вектор. Как, на ваш взгляд, функция должна отреагировать на это?
B>1. Всегда очищать (лишнее действие, что не в духе плюсов). B>2. Игнорировать, т.е. новые значения добавятся к старым ("не очистил сам дурак" — вполне в духе, но добавляет нестабильности). B>3. Должен сработать assert (типа assert(output.empty)). B>4. Вернуть ошибку/бросить исключение. B>5. Оставлю комментарий со своим вариантом.
B>(Хотел сделать настоящее голосование, но получаю "500 — Internal server error". Зато здесь можно обсудить более развернуто).
B>Что скажете?
Что скажу я?Конечно я не мегагуру но предполагаю , что перед GetSomething, нужно произвести некоторые действия,
думаю что идиома нподобие RAII в данном случае была-бы полезна.
Ну и какоето время пока GetSom жив всегда можно запросить копию объекта. Никаких исключений только пожалуйста и ассертов, разрушите объект почём зря.
struct GetSom
{
std::vector<Something> temp; Очень важно определиться что это за фигня этот Something? Если это POD тип то что делать примерно ясно.
B>При одном из вызовов функции передается непустой вектор. Как, на ваш взгляд, функция должна отреагировать на это?
B>1. Всегда очищать (лишнее действие, что не в духе плюсов). B>2. Игнорировать, т.е. новые значения добавятся к старым ("не очистил сам дурак" — вполне в духе, но добавляет нестабильности). B>3. Должен сработать assert (типа assert(output.empty)). B>4. Вернуть ошибку/бросить исключение. B>5. Оставлю комментарий со своим вариантом.
Это зависит от того, что должна делать функция и как предполагается её использовать.
Например, если функцию предполагается вызывать в цикле для пополнения вектора, то очевидно, что вариант 2.
Если — для инициализации вектора перед использованием, то 1, но если предполагается, что вектор должен передаваться пустым, то 3, а если есть уверенность, что очищается в другом месте или в другом месте добавляются нужные данные — 2.
Если есть какие-то ограничение на размер — 4, если ограничение нарушено.
Может ещё быть вариант, что параметр — in/out.
По любому способу должны быть комментарии, описывающие, что функция делает с параметром.