Возврат нескольких значений из функции C ++


242

Есть ли предпочтительный способ вернуть несколько значений из функции 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);

Является ли один из этих способов вообще предпочтительным, или есть другие предложения?

Изменить: в реальном коде, может быть более двух результатов. Они также могут быть разных типов.

Ответы:


217

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

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


12
+1 за кортеж. Имейте в виду последствия для производительности больших объектов, возвращающихся в структуре, по сравнению с передачей по ссылке.
Марчин

12
Если вы собираетесь использовать кортежи, почему бы не использовать их и для пар. Почему есть особый случай?
Ферруччо

4
Фред, да буст :: кортеж может это сделать :)
Йоханнес Шауб -

46
В C ++ 11 вы можете использовать std::tuple.
Ферруччо

14
Если вы хотите принять несколько значений из функции, удобный способ сделать это - использовать std::tie stackoverflow.com/a/2573822/502144
fdermishin

176

В 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;
}

или со структурами:

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;
}

4
У меня есть одна проблема с функциями, возвращающими кортежи. Скажем, вышеприведенный прототип функции находится в заголовке, тогда как мне узнать, что означают первое и второе возвращаемые значения, не понимая определения функции? фактор-остаток или остаток-фактор.
Учиа Итачи

7
@UchiaItachi То же самое касается параметров функций, вы можете давать им имена, но язык даже не применяет это, и имена параметров не имеют значения на сайте вызова при чтении. Кроме того, при одном возврате у вас просто есть тип, но наличие имени также может быть полезным, с кортежами вы просто удваиваете проблему, так что, по-моему, языку просто не хватает самодокументирования несколькими способами, не только этим.
pepper_chico

1
как бы выглядел последний пример, если бы я хотел явно указать тип возвращаемого значения div ()? Должен ли я определить результат в другом месте, или я могу определить его прямо в спецификации возвращаемого типа?
Слава

1
@ Слава, вы не можете определить тип прямо в сигнатуре функции, вам придется объявить тип снаружи и использовать его в качестве возвращаемого типа, как это обычно делается (просто переместите structстроку за пределы тела функции и замените autoвозвращаемую функцию на result.
pepper_chico

3
@pepper_chico Что если вы хотите поместить определение функции divideв отдельный файл cpp? Я получаю ошибку error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. Как мне это решить?
Адриан

123

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

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

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

Вот еще один пример кода, этот чуть менее тривиален:

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;

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

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


1
Предложение объявить structподобное Velocityприятно. Однако одна проблема заключается в том, что это загрязняет пространство имен. Я полагаю, что в C ++ 11 имя structможет иметь длинное имя типа, и его можно использовать auto result = calculateResultingVelocity(...).
Hugues

5
+1. Функция должна возвращать одну «вещь», а не как-то упорядоченный «кортеж вещей».
DevSolar

1
Я предпочитаю структуры над std :: pair / std :: tuples по причинам, описанным в этом ответе. Но мне также не нравится пространство имен "загрязнение". Идеальным решением для меня было бы возвращение анонимной структуры вроде struct { int a, b; } my_func();. Это может быть использовано , как это: auto result = my_func();. Но C ++ не допускает этого: «новые типы не могут быть определены в возвращаемом типе». Поэтому я должен создать такие структуры, как struct my_func_result_t...
anton_rh

2
@anton_rh: C ++ 14 позволяет возвращать локальные типы с помощью auto, так что auto result = my_func();это легко получить.
ildjarn

4
Около 15 лет назад, когда мы обнаружили повышение, мы часто использовали кортеж, так как он довольно удобен. С течением времени мы столкнулись с недостатком удобочитаемости, особенно для кортежей одного типа (например, кортеж <double, double>; какой из них какой). Поэтому в последнее время мы привыкли вводить небольшую структуру POD, где хотя бы имя переменной-члена указывает на что-то разумное.
gast128

24
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 - это, по сути, ваше структурное решение, но оно уже определено для вас и готово для адаптации к любым двум типам данных.


3
Это сработает для моего простого примера. В целом, однако, может быть возвращено более двух значений.
Фред Ларсон

5
Тоже не самодокументируемый. Можете ли вы вспомнить, какой регистр x86 является остатком для DIV?
Марк

1
@ Марк - Я согласен, что позиционные решения могут быть менее ремонтопригодны. Вы можете столкнуться с проблемой «переставлять и ставить в тупик».
Фред Ларсон

16

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

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

1
Мне нравится ваш ответ, и ваша последняя статья напоминает мне кое-что, что я только что прочитал, что передача по значению стала намного быстрее в зависимости от обстоятельств, делающих это более сложным ... cpp-next.com/archive/2009/08/want-speed-pass по значению
мудрец

12

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

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

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

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

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


10

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

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

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

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


10

С 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();
}

Хорошая вещь об этом - то, что это гарантированно не вызовет никакого копирования или перемещения. Вы можете сделать пример manystruct variadic тоже. Больше деталей:

Возвращение агрегатных переменных (структура) и синтаксиса для вариабельного шаблона C ++ 17 «Руководство по выводу конструкции»


6

Существует множество способов вернуть несколько параметров. Я собираюсь быть внимательным.

Используйте опорные параметры:

void foo( int& result, int& other_result );

использовать параметры указателя:

void foo( int* result, int* other_result );

что имеет преимущество в том, что вы должны сделать &вызов на сайте вызова, возможно, предупредив людей, что это выходной параметр.

Напишите шаблон и используйте его:

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
  out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(std::move(in)); }) {}
  out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
    target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
  template<class...Args> // TODO: SFINAE enable_if test
  void emplace(Args&&...args) {
    target( T(std::forward<Args>(args)...) );
  }
  template<class X> // TODO: SFINAE enable_if test
  void operator=(X&&x){ emplace(std::forward<X>(x)); }
  template<class...Args> // TODO: SFINAE enable_if test
  void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};

тогда мы можем сделать:

void foo( out<int> result, out<int> other_result )

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

Другие способы определения места, в которое вы можете поместить данные, могут быть использованы для построения out. Например, обратный вызов для размещения чего-либо.

Мы можем вернуть структуру:

struct foo_r { int result; int other_result; };
foo_r foo();

Whick работает нормально в каждой версии C ++, и в это также позволяет:

auto&&[result, other_result]=foo();

по нулевой цене. Параметры даже не могут быть перемещены благодаря гарантированному выбору.

Мы могли бы вернуть std::tuple:

std::tuple<int, int> foo();

недостаток в том, что параметры не названы. Это позволяет:

auto&&[result, other_result]=foo();

также. До вместо этого мы можем сделать:

int result, other_result;
std::tie(result, other_result) = foo();

что немного более неловко. Однако здесь гарантированное разрешение не работает.

Зайдя на чужую территорию (а это после out<>!), Мы можем использовать стиль продолжения:

void foo( std::function<void(int result, int other_result)> );

и теперь звонящие делают:

foo( [&](int result, int other_result) {
  /* code */
} );

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

void get_all_values( std::function<void(int)> value )

valueобратный вызов можно назвать 500 раз , когда вы get_all_values( [&](int value){} ).

Для чистого безумия вы можете даже использовать продолжение в продолжении.

void foo( std::function<void(int, std::function<void(int)>)> result );

чье использование выглядит так:

foo( [&](int result, auto&& other){ other([&](int other){
  /* code */
}) });

что позволило бы много-один отношения между resultи other.

Опять же со значениями Uniforn, мы можем сделать это:

void foo( std::function< void(span<int>) > results )

здесь мы вызываем обратный вызов с диапазоном результатов. Мы даже можем сделать это неоднократно.

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

void foo( std::function< void(span<int>) > results ) {
  int local_buffer[1024];
  std::size_t used = 0;
  auto send_data=[&]{
    if (!used) return;
    results({ local_buffer, used });
    used = 0;
  };
  auto add_datum=[&](int x){
    local_buffer[used] = x;
    ++used;
    if (used == 1024) send_data();
  };
  auto add_data=[&](gsl::span<int const> xs) {
    for (auto x:xs) add_datum(x);
  };
  for (int i = 0; i < 7+(1<<20); ++i) {
    add_datum(i);
  }
  send_data(); // any leftover
}

Теперь std::functionэто немного тяжело, так как мы будем делать это в средах без выделения ресурсов с нулевыми накладными расходами. Поэтому мы бы хотели, чтобы это function_viewникогда не выделялось.

Другое решение:

std::function<void(std::function<void(int result, int other_result)>)> foo(int input);

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

foo (7) ([&] (int result, int other_result) {/ * code * /}); это отделяет выходные параметры от входных параметров, используя отдельные скобки.

С variantисопрограммы, вы могли бы сделать fooгенератор варианта возвращаемых типов (или просто возвращаемый тип). Синтаксис еще не установлен, поэтому я не буду приводить примеры.

В мире сигналов и слотов, функция, которая выставляет набор сигналов:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

позволяет создать fooасинхронную работу, которая транслирует результат после его завершения.

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

foo( int_source )( int_dest1, int_dest2 );

тогда этот код ничего не делает, пока int_sourceне предоставит целые числа для его предоставления. Когда это произойдет, int_dest1и int_dest2начните получать результаты.


Этот ответ содержит больше информации, чем другие ответы! в частности, информация о auto&&[result, other_result]=foo();функциях, возвращающих как кортежи, так и структуры. Спасибо!
19

Я ценю этот исчерпывающий ответ, тем более что я все еще застрял в C ++ 11 и поэтому не могу использовать некоторые из более современных решений, которые предлагают другие люди.
GuyGizmo

5

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

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

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


4

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

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


4

Здесь я пишу программу, которая возвращает несколько значений (более двух значений) в 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 может возвращать более двух значений.


4
В C ++ 14 вы также можете использовать autoтип возврата, calчтобы сделать его еще чище. (ИМО).
sfjac

3

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


2

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

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

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


2

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

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

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

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

Ваши параметры сбивают с толку? Параметр, отправленный как ссылка, предполагает изменение значения (в отличие от константной ссылки). Разумное именование также устраняет путаницу.


1
Я думаю, что это немного сбивает с толку. Кто-то, читающий код, который вызывает его, видит «разделяем (a, b, c);». Там нет никаких признаков того, что с является outval, пока они не ищут подпись. Но это общий страх неконстантных ссылочных параметров, а не конкретный вопрос.
Стив Джессоп

2

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

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

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};

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

1
@jiggunjer Вы можете запустить цикл один раз и сохранить несколько возвращаемых значений в отдельных элементах данных класса. Это подчеркивает гибкость концепции ООП.
Роланд

2

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

int divide(int a,int b,int quo,int &rem)

Разве я не упомянул об этом в самом вопросе? Также посмотрите мои возражения в моем ответе .
Фред Ларсон

1

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

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

include "boost/tuple/tuple.hpp"

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

{
  return make_tuple(dividend / divisor,dividend % divisor )
}

1

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


1

Я бы просто сделал это по ссылке, если это только несколько возвращаемых значений, но для более сложных типов вы также можете просто сделать это так:

static struct SomeReturnType {int a,b,c; string str;} SomeFunction()
{
  return {1,2,3,string("hello world")}; // make sure you return values in the right order!
}

используйте «static», чтобы ограничить область действия возвращаемого типа этим модулем компиляции, если он предназначен только для временного возвращаемого типа.

 SomeReturnType st = SomeFunction();
 cout << "a "   << st.a << endl;
 cout << "b "   << st.b << endl;
 cout << "c "   << st.c << endl;
 cout << "str " << st.str << endl;

Это определенно не самый красивый способ сделать это, но это сработает.


-2

Вот полный пример такого рода решения проблемы

#include <bits/stdc++.h>
using namespace std;
pair<int,int> solve(int brr[],int n)
{
    sort(brr,brr+n);

    return {brr[0],brr[n-1]};
}

int main()
{
    int n;
    cin >> n;
    int arr[n];
    for(int i=0; i<n; i++)
    {
        cin >> arr[i];
    }

    pair<int,int> o=solve(arr,n);
    cout << o.first << " " << o.second << endl;

    return 0;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.