Есть ли предпочтительный способ вернуть несколько значений из функции C ++? Например, представьте себе функцию, которая делит два целых числа и возвращает как частное, так и остаток. Один из способов, которые я обычно вижу, это использование опорных параметров:
void divide(int dividend, int divisor, int& quotient, int& remainder);
Вариант состоит в том, чтобы вернуть одно значение и передать другое через опорный параметр:
int divide(int dividend, int divisor, int& remainder);
Другой способ - объявить структуру, содержащую все результаты, и вернуть следующее:
struct divide_result {
int quotient;
int remainder;
};
divide_result divide(int dividend, int divisor);
Является ли один из этих способов вообще предпочтительным, или есть другие предложения?
Изменить: в реальном коде, может быть более двух результатов. Они также могут быть разных типов.
std::tuple
.std::tie
stackoverflow.com/a/2573822/502144В C ++ 11 вы можете:
В C ++ 17:
или со структурами:
источник
struct
строку за пределы тела функции и заменитеauto
возвращаемую функцию наresult
.divide
в отдельный файл cpp? Я получаю ошибкуerror: use of ‘auto divide(int, int)’ before deduction of ‘auto’
. Как мне это решить?Лично мне вообще не нравятся возвращаемые параметры по ряду причин:
У меня также есть некоторые оговорки о технике пар / кортежей. В основном, часто нет естественного порядка возвращаемых значений. Как читатель кода может узнать, является ли result.first частным или остатком? И разработчик может изменить порядок, что нарушит существующий код. Это особенно коварно, если значения имеют один и тот же тип, чтобы не возникало ошибок или предупреждений компилятора. На самом деле эти аргументы применимы и к возвращаемым параметрам.
Вот еще один пример кода, этот чуть менее тривиален:
Имеет ли этот отпечаток скорость и курс или курс и скорость? Это не очевидно.
Сравните с этим:
Я думаю, что это понятнее.
Так что я думаю, что мой первый выбор в целом - это техника struct. Идея пары / кортежа, вероятно, является отличным решением в некоторых случаях. Я хотел бы избежать возвращаемых параметров, когда это возможно.
источник
struct
подобноеVelocity
приятно. Однако одна проблема заключается в том, что это загрязняет пространство имен. Я полагаю, что в C ++ 11 имяstruct
может иметь длинное имя типа, и его можно использоватьauto result = calculateResultingVelocity(...)
.struct { int a, b; } my_func();
. Это может быть использовано , как это:auto result = my_func();
. Но C ++ не допускает этого: «новые типы не могут быть определены в возвращаемом типе». Поэтому я должен создать такие структуры, какstruct my_func_result_t
...auto
, так чтоauto result = my_func();
это легко получить.std :: pair - это, по сути, ваше структурное решение, но оно уже определено для вас и готово для адаптации к любым двум типам данных.
источник
Это полностью зависит от фактической функции и значения нескольких значений, а также их размеров:
источник
Решение OO для этого состоит в том, чтобы создать класс отношений. Это не потребовало бы никакого дополнительного кода (сэкономило бы немного), было бы значительно чище / яснее, и дало бы вам некоторые дополнительные рефакторинги, позволяющие вам очистить код и вне этого класса.
На самом деле я думаю, что кто-то порекомендовал вернуть структуру, которая достаточно близка, но скрывает намерение, что это должен быть полностью продуманный класс с конструктором и несколькими методами, фактически «метод», который вы изначально упоминали (как возвращающий pair), скорее всего, должен быть членом этого класса, возвращая свой экземпляр.
Я знаю, что ваш пример был просто «Примером», но факт в том, что если ваша функция не выполняет больше, чем любая другая функция, если вы хотите, чтобы она возвращала несколько значений, вы почти наверняка пропускаете объект.
Не бойтесь создавать эти крошечные классы, чтобы выполнять маленькие кусочки работы - это волшебство ОО - вы заканчиваете тем, что разбиваете его на части, пока каждый метод не станет очень маленьким и простым, а каждый класс - маленьким и понятным.
Еще одна вещь, которая должна была указывать на то, что что-то не так: в OO у вас практически нет данных - OO - это не передача данных, а класс, который должен управлять своими собственными данными внутри себя, передавать любые данные (включая методы доступа) это признак того, что вам может понадобиться что-то переосмыслить ..
источник
Существует прецедент для возвращения структур в C (и , следовательно , C ++) стандарт с
div
,ldiv
(и, в C99,lldiv
) функции из<stdlib.h>
(или<cstdlib>
).«Сочетание возвращаемого значения и возвращаемых параметров» обычно наименее чистое.
Наличие функции, возвращающей статус и возвращающей данные через возвращаемые параметры, имеет смысл в C; это менее очевидно в C ++, где вы могли бы вместо этого использовать исключения для передачи информации об ошибках.
Если существует более двух возвращаемых значений, то лучше всего подходит механизм, подобный структуре.
источник
С C ++ 17 вы также можете вернуть одно или более неподвижных / не копируемых значений (в некоторых случаях). Возможность возвращать неподвижные типы обеспечивается за счет новой оптимизации гарантированного возвращаемого значения, которая хорошо сочетается с агрегатами и так называемыми шаблонными конструкторами .
Хорошая вещь об этом - то, что это гарантированно не вызовет никакого копирования или перемещения. Вы можете сделать пример
many
struct variadic тоже. Больше деталей:Возвращение агрегатных переменных (структура) и синтаксиса для вариабельного шаблона C ++ 17 «Руководство по выводу конструкции»
источник
Существует множество способов вернуть несколько параметров. Я собираюсь быть внимательным.
Используйте опорные параметры:
использовать параметры указателя:
что имеет преимущество в том, что вы должны сделать
&
вызов на сайте вызова, возможно, предупредив людей, что это выходной параметр.Напишите шаблон и используйте его:
тогда мы можем сделать:
и все хорошо.
foo
больше не может читать любые значения, переданные в качестве бонуса.Другие способы определения места, в которое вы можете поместить данные, могут быть использованы для построения
out
. Например, обратный вызов для размещения чего-либо.Мы можем вернуть структуру:
Whick работает нормально в каждой версии C ++, и в C ++ 17 это также позволяет:
по нулевой цене. Параметры даже не могут быть перемещены благодаря гарантированному выбору.
Мы могли бы вернуть
std::tuple
:недостаток в том, что параметры не названы. Это позволяетC ++ 17:
также. ДоC ++ 17 вместо этого мы можем сделать:
что немного более неловко. Однако здесь гарантированное разрешение не работает.
Зайдя на чужую территорию (а это после
out<>
!), Мы можем использовать стиль продолжения:и теперь звонящие делают:
Преимущество этого стиля в том, что вы можете возвращать произвольное количество значений (с единообразным типом) без необходимости управления памятью:
value
обратный вызов можно назвать 500 раз , когда выget_all_values( [&](int value){} )
.Для чистого безумия вы можете даже использовать продолжение в продолжении.
чье использование выглядит так:
что позволило бы много-один отношения между
result
иother
.Опять же со значениями Uniforn, мы можем сделать это:
здесь мы вызываем обратный вызов с диапазоном результатов. Мы даже можем сделать это неоднократно.
Используя это, вы можете иметь функцию, которая эффективно передает мегабайты данных без какого-либо выделения из стека.
Теперь
std::function
это немного тяжело, так как мы будем делать это в средах без выделения ресурсов с нулевыми накладными расходами. Поэтому мы бы хотели, чтобы этоfunction_view
никогда не выделялось.Другое решение:
где вместо того, чтобы принимать обратный вызов и вызывать его,
foo
вместо этого возвращается функция, которая принимает обратный вызов.foo (7) ([&] (int result, int other_result) {/ * code * /}); это отделяет выходные параметры от входных параметров, используя отдельные скобки.
С
variant
иC ++ 20сопрограммы, вы могли бы сделатьfoo
генератор варианта возвращаемых типов (или просто возвращаемый тип). Синтаксис еще не установлен, поэтому я не буду приводить примеры.В мире сигналов и слотов, функция, которая выставляет набор сигналов:
позволяет создать
foo
асинхронную работу, которая транслирует результат после его завершения.Вниз по этой линии у нас есть множество методов конвейерной обработки, когда функция не делает что-то, а скорее организует подключение данных каким-либо образом, а выполнение относительно независимо.
тогда этот код ничего не делает, пока
int_source
не предоставит целые числа для его предоставления. Когда это произойдет,int_dest1
иint_dest2
начните получать результаты.источник
auto&&[result, other_result]=foo();
функциях, возвращающих как кортежи, так и структуры. Спасибо!Используйте структуру или класс для возвращаемого значения. Использование
std::pair
может работать на данный момент, ноВозвращение структуры с самодокументируемыми именами переменных-членов, вероятно, будет менее подвержено ошибкам для тех, кто использует вашу функцию. Надев на мгновение мою коллегу,
divide_result
я, потенциальный пользователь вашей функции, сразу пойму вашу структуру, чтобы понять ее через 2 секунды. Бездействие с выходными параметрами или таинственными парами и кортежами займет больше времени для чтения и может быть использовано неправильно. И, скорее всего, даже после использования функции несколько раз, я все еще не буду помнить правильный порядок аргументов.источник
Если ваша функция возвращает значение через ссылку, компилятор не может сохранить его в регистре при вызове других функций, потому что, теоретически, первая функция может сохранить адрес переменной, переданной ей, в глобально доступной переменной, и любые вызываемые соответственно функции могут измените его, чтобы компилятор (1) сохранял значение из регистров обратно в память перед вызовом других функций и (2) перечитывал его, когда это необходимо из памяти снова после любого из таких вызовов.
Если вы вернетесь по ссылке, оптимизация вашей программы пострадает
источник
Здесь я пишу программу, которая возвращает несколько значений (более двух значений) в C ++. Эта программа исполняется на C ++ 14 (G ++ 4.9.2). Программа похожа на калькулятор.
Таким образом, вы можете четко понимать, что таким образом вы можете возвращать несколько значений из функции. при использовании std :: pair могут быть возвращены только 2 значения, а std :: tuple может возвращать более двух значений.
источник
auto
тип возврата,cal
чтобы сделать его еще чище. (ИМО).Я склонен использовать out-vals в подобных функциях, потому что я придерживаюсь парадигмы функции, возвращающей коды успеха / ошибок, и мне нравится сохранять единообразие.
источник
Альтернативы включают массивы, генераторы и инверсию управления , но ни один из них не подходит здесь.
Некоторые (например, Microsoft в историческом Win32) склонны использовать ссылочные параметры для простоты, потому что ясно, кто выделяет и как это будет выглядеть в стеке, уменьшает распространение структур и допускает отдельное возвращаемое значение для успеха.
«Чистые» программисты предпочитают - структуру, предполагая , что это значение функции (как в данном случае), а не то , что прикоснулся к слову функции. Если бы у вас была более сложная процедура или что-то с состоянием, вы, вероятно, использовали бы ссылки (при условии, что у вас есть причина не использовать класс).
источник
Я бы сказал, что нет предпочтительного метода, все зависит от того, что вы собираетесь делать с ответом. Если результаты будут использоваться вместе в дальнейшей обработке, тогда структуры имеют смысл, если нет, то я буду склонен передавать их как отдельные ссылки, если только функция не будет использоваться в составном операторе:
x = divide( x, y, z ) + divide( a, b, c );
Я часто предпочитаю передавать «структуры» по ссылке в списке параметров, вместо того, чтобы накладывать на копию при возврате новую структуру (но это очень неприятно).
void divide(int dividend, int divisor, Answer &ans)
Ваши параметры сбивают с толку? Параметр, отправленный как ссылка, предполагает изменение значения (в отличие от константной ссылки). Разумное именование также устраняет путаницу.
источник
Почему вы настаиваете на функции с несколькими возвращаемыми значениями? С ООП вы можете использовать класс, предлагающий обычную функцию с одним возвращаемым значением и любым количеством дополнительных «возвращаемых значений», как показано ниже. Преимущество заключается в том, что у вызывающего абонента есть выбор просмотра дополнительных элементов данных, но он не обязан это делать. Это предпочтительный метод для сложных баз данных или сетевых вызовов, где может потребоваться много дополнительной информации о возврате в случае возникновения ошибок.
Чтобы ответить на ваш первоначальный вопрос, в этом примере есть метод, возвращающий частное, то есть то, что может понадобиться большинству вызывающих, и дополнительно, после вызова метода, вы можете получить остаток в качестве члена данных.
источник
вместо того, чтобы возвращать несколько значений, просто верните одно из них и сделайте ссылку на другие в требуемой функции, например:
источник
Boost-кортеж - мой предпочтительный выбор для обобщенной системы возврата более одного значения из функции.
Возможный пример:
источник
Мы можем объявить функцию так, чтобы она возвращала пользовательскую переменную структурного типа или указатель на нее. А по свойству структуры мы знаем, что структура в C может содержать несколько значений асимметричных типов (то есть одну переменную типа int, четыре переменные типа char, две переменные типа float и т. Д.)
источник
Я бы просто сделал это по ссылке, если это только несколько возвращаемых значений, но для более сложных типов вы также можете просто сделать это так:
используйте «static», чтобы ограничить область действия возвращаемого типа этим модулем компиляции, если он предназначен только для временного возвращаемого типа.
Это определенно не самый красивый способ сделать это, но это сработает.
источник
Вот полный пример такого рода решения проблемы
источник