Как вернуть два вектора из одной функции C ++ [duplicate]

179
задан Fred Larson 26 November 2008 в 16:38
поделиться

18 ответов

Для возврата двух значений я использую std::pair (обычно typedef'd). Вы должны посмотреть на boost::tuple (в C ++ 11 и новее, есть std::tuple) для более чем двух результатов возврата.

С введением структурированного связывания в C ++ 17 возвращение std::tuple должно, вероятно, стать принятым стандарт.

153
ответ дан SergeyA 4 September 2018 в 07:45
поделиться
  • 1
    +1 для кортежа. Имейте в виду разветвление результатов больших объектов, возвращающихся в структуру, или передачу по ссылке. – Marcin 26 November 2008 в 16:40
  • 2
    Если вы собираетесь использовать кортежи, почему бы и не использовать их для пар. Почему есть специальный случай? – Ferruccio 26 November 2008 в 16:44
  • 3
    Фред, да boost :: tuple может это сделать :) – Johannes Schaub - litb 26 November 2008 в 16:51
  • 4
    В C ++ 11 вы можете использовать std::tuple. – Ferruccio 20 October 2011 в 11:32
  • 5
    Если вы хотите принять несколько значений из функции, удобный способ сделать это - использовать std::tie stackoverflow.com/a/2573822/502144 – fdermishin 12 May 2014 в 09:44

Решение OO для этого состоит в создании класса отношения. Это не потребует никакого дополнительного кода (что бы сэкономить), было бы значительно чище / яснее и предоставило бы вам некоторые дополнительные рефакторинги, позволяющие очищать код вне этого класса.

На самом деле я думаю, что кто-то рекомендуется вернуть структуру, которая достаточно близка, но скрывает намерение, что это должен быть полностью продуманный класс с конструктором и несколькими методами, по сути, «метод», который вы первоначально упоминали (как возвращение пары), должен вероятно, будет членом этого класса, возвращая экземпляр самого себя.

Я знаю, что ваш пример был просто «примером», но факт в том, что, если ваша функция не делает больше, чем любая функция должна делать, Если вы хотите, чтобы он возвращал несколько значений, вы почти наверняка потеряли объект.

Не бойтесь создавать эти крошечные классы, чтобы делать небольшие куски работы - это волшебство OO - вы заканчиваете до тех пор, пока каждый метод не будет очень маленьким и простым, а каждый класс будет небольшим и понятным.

Еще одна вещь, которая должна была быть индикатором того, что что-то не так: в OO у вас практически нет данных - OO не собирается передавать данные, классу нужно управлять и манипулировать собственными данными внутри, любые передачи данных (включая аксессоры) являются признаком того, что вам может потребоваться переосмыслить что-то ..

11
ответ дан Bill K 4 September 2018 в 07:45
поделиться

С C ++ 17 вы также можете вернуть одно или более неизменяемых / непокрытых значений (в некоторых случаях). Возможность возврата неизменяемых типов осуществляется с помощью новой гарантированной оптимизации возвращаемого значения, и она прекрасно сочетается с агрегатами и тем, что можно назвать конструкторами шаблона .

template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};

// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){
   // in place construct x,y,z with a string, 5.7 and unmovable.
   auto [x,y,z] = f();
}

Симпатичная вещь в том, что гарантировано не вызвать любого копирования или перемещения. Вы можете сделать пример many struct variadic тоже. Дополнительная информация:

Возвращение переменных адатов (struct) и синтаксиса для C ++ 17 Variadic template «Руководство по выведению из строя»

9
ответ дан Community 4 September 2018 в 07:45
поделиться

Используйте конструкцию или класс для возвращаемого значения. Использование std::pair может работать пока, но

  1. негибко, если вы решите позже, что хотите получить больше информации;
  2. не совсем ясно из объявления функции в заголовке, что возвращается и в каком порядке.

Возвращение структуры с самодокументируемыми именами переменных переменных вероятнее всего быть менее подверженным ошибкам для тех, кто использует вашу функцию. Надевая шляпу моего сотрудника на мгновение, ваша структура divide_result легко для меня, потенциального пользователя вашей функции, сразу же понять через 2 секунды. Посылка с параметрами вывода или таинственными парами и кортежами потребует больше времени для чтения и может быть использована неправильно. И, скорее всего, даже после использования функции несколько раз я до сих пор не помню правильный порядок аргументов.

4
ответ дан DIF 4 September 2018 в 07:45
поделиться

Если ваша функция возвращает значение через ссылку, компилятор не может сохранить его в регистре при вызове других функций, потому что теоретически первая функция может сохранить адрес переменной, переданной ей в глобально доступной переменной, и любой последующий вызываемые функции могут изменить его, поэтому компилятор будет иметь (1) сохранить значение из регистров обратно в память перед вызовом других функций и (2) перечитать его, когда это необходимо из памяти, после любого из таких вызовов.

Если вы вернетесь по ссылке, оптимизация вашей программы будет страдать

3
ответ дан dmityugov 4 September 2018 в 07:45
поделиться

Лично мне обычно не нравятся возвращаемые параметры по ряду причин:

  • в вызове не всегда очевидно, какие параметры являются ins и какие выходы
  • вы как правило, нужно создать локальную переменную, чтобы поймать результат, в то время как возвращаемые значения могут использоваться inline (что может быть или не быть хорошей идеей, но по крайней мере у вас есть опция)
  • кажется мне чище иметь «дверь» и «выходную дверь» к функции - все входы идут здесь, все выходы выходят там
  • Мне нравится держать списки аргументов как можно короче

У меня также есть некоторые оговорки в отношении метода пары / кортежа. Главным образом, часто нет естественного порядка для возвращаемых значений. Как читатель кода узнает, является ли результат. Первым является фактор или остаток? И разработчик мог бы изменить порядок, который нарушил бы существующий код. Это особенно коварно, если значения одного типа, так что никакая ошибка компилятора или предупреждение не будут сгенерированы. Фактически, эти аргументы применимы и к возвращаемым параметрам.

Вот еще один пример кода, этот бит немного менее тривиальный:

pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth,
                                               double planeAirspeed, double planeCourse);

pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.first << endl;
cout << result.second << endl;

Выполняет ли это печать на земле и курс, или курс и путевая? Это не очевидно.

Сравните с этим:

struct Velocity {
    double speed;
    double azimuth;
};
Velocity calculateResultingVelocity(double windSpeed, double windAzimuth,
                                    double planeAirspeed, double planeCourse);

Velocity result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.speed << endl;
cout << result.azimuth << endl;

Я думаю, что это яснее.

Итак, я считаю, что мой первый выбор в целом - это метод построения , Идея пары / кортежа, вероятно, является отличным решением в некоторых случаях. Я хотел бы избежать возможных параметров возврата.

109
ответ дан Fred Larson 4 September 2018 в 07:45
поделиться
  • 1
    Предложение объявить struct, подобное Velocity, является хорошим. Однако одна проблема заключается в том, что она загрязняет пространство имен. Я полагаю, что с C ++ 11 struct может иметь длинное имя типа, и можно использовать auto result = calculateResultingVelocity(...). – Hugues 12 February 2013 в 07:06
  • 2
    +1. Функция должна возвращать one «вещь», а не как-то упорядоченный «набор вещей». – DevSolar 13 May 2013 в 08:12
  • 3
    Я предпочитаю структуры над std :: pairs / std :: tuples по причинам, описанным в этом ответе. Но мне не нравится пространство имен и «загрязнение». также. Идеальное решение для меня будет возвращать анонимную структуру, например struct { int a, b; } my_func();. Это можно использовать как это: auto result = my_func();. Но C ++ не позволяет этого: «новые типы не могут быть определены в возвращаемом типе». Поэтому мне нужно создать такие структуры, как struct my_func_result_t ... – anton_rh 21 April 2016 в 06:14
  • 4
    @anton_rh: C ++ 14 позволяет возвращать локальные типы с помощью auto, поэтому auto result = my_func(); тривиально можно получить. – ildjarn 15 January 2017 в 02:11
  • 5
    Примерно 15 лет назад, когда мы обнаружили повышение, мы много использовали кортеж, так как это очень удобно. Сверхурочные мы испытывали недостаток в удобочитаемости, особенно для кортежей, имеющих один и тот же тип (например, кортеж & lt; double, double & gt ;; какой из них). Поэтому в последнее время мы привыкли вводить небольшую структуру POD, где, по крайней мере, имя переменной-члена указывает что-то разумное. – gast128 14 August 2017 в 15:19
std::pair<int, int> divide(int dividend, int divisor)
{
   // :
   return std::make_pair(quotient, remainder);
}

std::pair<int, int> answer = divide(5,2);
 // answer.first == quotient
 // answer.second == remainder

std :: pair - это, по сути, ваше структурное решение, но уже определенное для вас и готовое к адаптации к любым двум типам данных.

21
ответ дан James Curran 4 September 2018 в 07:45
поделиться
  • 1
    Это будет работать для моего простого примера. Однако в общем случае может быть возвращено более двух значений. – Fred Larson 26 November 2008 в 16:27
  • 2
    Также не самодокументируется. Можете ли вы вспомнить, какой регистр x86 является остатком для DIV? – Mark 26 November 2008 в 16:35
  • 3
    @Mark - Я согласен с тем, что позиционные решения могут быть менее удобными. Вы можете столкнуться с «перестановкой и перегородкой». проблема. – Fred Larson 26 November 2008 в 16:42

Я склонен использовать out-vals в таких функциях, потому что я придерживаюсь парадигмы функции, возвращающей коды успеха / ошибки, и мне нравится сохранять вещи одинаковыми.

2
ответ дан John Dibling 4 September 2018 в 07:45
поделиться

Существует прецедент для возврата структур в стандарте C (и, следовательно, C ++) с функциями div, ldiv (и в C99, lldiv) из <stdlib.h> (или <cstdlib>).

«Смешение возвращаемого значения и возвращаемых параметров» обычно является наименее чистым.

Наличие функции возвращает статус и возвращает данные через возвращаемые параметры в C; это менее очевидно в C ++, где вы могли бы использовать исключения для ретрансляции информации об отказах.

Если имеется более двух возвращаемых значений, то, вероятно, лучше всего подходит структура-подобный механизм.

8
ответ дан Jonathan Leffler 4 September 2018 в 07:45
поделиться

Здесь я пишу программу, которая возвращает несколько значений (более двух значений) в c ++. Эта программа является исполняемой в c ++ 14 (G ++ 4.9.2). Программа похожа на калькулятор.

#  include <tuple>
# include <iostream>

using namespace std; 

tuple < int,int,int,int,int >   cal(int n1, int n2)
{
    return  make_tuple(n1/n2,n1%n2,n1+n2,n1-n2,n1*n2);
}

int main()
{
    int qut,rer,add,sub,mul,a,b;
    cin>>a>>b;
    tie(qut,rer,add,sub,mul)=cal(a,b);
    cout << "quotient= "<<qut<<endl;
    cout << "remainder= "<<rer<<endl;
    cout << "addition= "<<add<<endl;
    cout << "subtraction= "<<sub<<endl;
    cout << "multiplication= "<<mul<<endl;
    return 0;
}

Итак, вы можете четко понимать, что таким образом вы можете вернуть несколько значений из функции. используя std :: pair, могут возвращаться только 2 значения, а std :: tuple может возвращать более двух значений.

3
ответ дан M.M 4 September 2018 в 07:45
поделиться
  • 1
    С C ++ 14 вы также можете использовать тип возврата auto на cal, чтобы сделать это еще более чистым. (ИМО). – sfjac 19 April 2015 в 14:53

вместо того, чтобы возвращать несколько значений, просто верните один из них и сделайте ссылку других в требуемой функции, например:

int divide(int a,int b,int quo,int &rem)
1
ответ дан Machavity 4 September 2018 в 07:45
поделиться

Альтернативы включают в себя массивы, генераторы и инверсию управления , но здесь не подходит.

Некоторые (например, Microsoft в историческом Win32) имеют тенденцию использовать ссылочные параметры для простоты, потому что ясно, кто распределяет и как он будет выглядеть в стеке, уменьшает распространение структур и позволяет получить отдельное возвращаемое значение для успеха.

«Чистые» программисты предпочитают структуру , предполагая, что - значение функции (как это имеет место здесь), а не то, что коснулось функции. Если у вас была более сложная процедура или что-то с состоянием, вы, вероятно, использовали бы ссылки (при условии, что у вас есть причина не использовать класс).

1
ответ дан Mark 4 September 2018 в 07:45
поделиться

Я бы сказал, что нет предпочтительного метода, все зависит от того, что вы собираетесь делать с ответом. Если результаты будут использоваться вместе в дальнейшей обработке, тогда структуры имеют смысл, если бы я не стал передавать их в качестве отдельных ссылок, если бы функция не использовалась в составном заявлении:

x = divide( x, y, z ) + divide( a, b, c );

Я часто предпочитаю передавать «внешние структуры» по ссылке в списке параметров, вместо того, чтобы иметь пропуск накладных расходов на копирование новой структуры (но это потение малым материалом).

void divide(int dividend, int divisor, Answer &ans)

Изменились ли параметры? Параметр, отправленный как ссылка, указывает на то, что значение изменится (в отличие от ссылки на константу). Разумное именование также устраняет путаницу.

1
ответ дан Patrick 4 September 2018 в 07:45
поделиться
  • 1
    Я думаю, что это немного запутанно. Кто-то читает код, который его вызывает, видит «делить (a, b, c)» и «. Нет никаких признаков того, что c - это аутвал, пока они не поднимут подпись. Но это общий страх перед неконстантными ссылочными параметрами, а не особый к этому вопросу. – Steve Jessop 26 November 2008 в 22:51

В C ++ 11 вы можете:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  std::make_tuple(dividend / divisor, dividend % divisor);
}

#include <iostream>

int main() {
    using namespace std;

    int quotient, remainder;

    tie(quotient, remainder) = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

В C ++ 17:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

или с помощью структур:

#include <tuple>

auto divide(int dividend, int divisor) {
    struct result {int quotient; int remainder;};
    return result {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto result = divide(14, 3);

    cout << result.quotient << ',' << result.remainder << endl;

    // or

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}
104
ответ дан pepper_chico 4 September 2018 в 07:45
поделиться
  • 1
    У меня есть одна проблема с функциями, возвращающими кортежи. Скажем, прототип функции выше в заголовке, а затем как узнать, что означает первое и второе возвращаемые значения, не понимая определения функции? фактор-остаток или остаток. – Uchia Itachi 4 November 2016 в 08:22
  • 2
    @UchiaItachi То же самое касается параметров функции, вы можете дать им имена, но язык даже не применяет это, а имена параметров не имеют значения на сайте вызова при чтении. Кроме того, при одном возврате у вас просто есть тип, но имя может также быть полезным, с кортежами вы просто удваиваете проблему, поэтому на языке, который просто отсутствует, в отношении того, что он сам документирован несколькими способами, а не только это. – pepper_chico 20 January 2017 в 16:21
  • 3
    как бы выглядел последний пример, если бы я хотел явно указать возвращаемый тип divide ()? Должен ли я затем определить результат где-то еще, или я могу определить его правильно в спецификации типа возврата? – Slava 11 July 2017 в 13:30
  • 4
    @Slava вы не можете определить тип права в сигнатуре функции, вам придется объявить тип снаружи и использовать его как возвращаемый тип, как это обычно делается (просто переместите строку struct за пределы тела функции и замените auto возвращать функцию с помощью result. – pepper_chico 11 July 2017 в 15:43
  • 5
    @pepper_chico Что делать, если хотите поместить определение функции divide в отдельный файл cpp? Я получаю ошибку error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. Как я могу это решить? – Adriaan 23 February 2018 в 21:26

Boost tuple был бы моим предпочтительным выбором для обобщенной системы возврата более одного значения из функции.

Возможный пример:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}
0
ответ дан PRAFUL ANAND 4 September 2018 в 07:45
поделиться

Мы можем объявить такую ​​функцию, чтобы она возвращала определенную пользователем структуру типа или указатель на нее. И по свойству структуры мы знаем, что структура в C может содержать несколько значений асимметричных типов (т. Е. Одна переменная int, четыре переменные char, две переменные float и т. Д.)

0
ответ дан Rohit Hajare 4 September 2018 в 07:45
поделиться

Почему вы настаиваете на функции с несколькими возвращаемыми значениями? С помощью OOP вы можете использовать класс, предлагающий регулярную функцию с единственным возвращаемым значением, и любое количество дополнительных «возвращаемых значений», как показано ниже. Преимущество заключается в том, что у вызывающего есть возможность взглянуть на дополнительные элементы данных, но это не требуется для этого. Это предпочтительный метод для сложной базы данных или сетевых вызовов, где может потребоваться много дополнительной информации о возврате в случае возникновения ошибок.

Чтобы ответить на ваш исходный вопрос, в этом примере есть метод возврата частного, это то, что может понадобиться большинству абонентов, и, кроме того, после вызова метода вы можете получить остаток в качестве элемента данных.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};
0
ответ дан Roland 4 September 2018 в 07:45
поделиться
  • 1
    Слишком плохо, что downvoter не хотел рассказывать нам, почему мое решение не полезно. К счастью, мне очень нравилось использовать этот метод много раз сам. – Roland 10 February 2015 в 12:07
  • 2
    Я думаю, что есть случаи, когда это неэффективно. Например. у вас есть одиночный цикл, который генерирует несколько возвращаемых значений. Если вы разделите эти значения на отдельные функции, вам нужно будет пропустить цикл за один раз для каждого значения. – jiggunjer 8 March 2015 в 18:42
  • 3
    @jiggunjer Вы можете запустить цикл один раз и сохранить несколько возвращаемых значений в отдельных членах данных класса. Это подчеркивает гибкость концепции ООП. – Roland 9 March 2015 в 13:50

Это полностью зависит от фактической функции и значения нескольких значений и их размеров:

  • Если они связаны как в примере с вашей долей, то я бы пошел с struct или class instance.
  • Если они не связаны друг с другом и не могут быть сгруппированы в класс / struct, возможно, вам необходимо реорганизовать ваш метод на два.
  • В зависимости от размер памяти в значениях, которые вы возвращаете, вы можете захотеть вернуть указатель на экземпляр класса или структуру или использовать ссылочные параметры.
14
ответ дан Stewart Johnson 4 September 2018 в 07:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: