Перейти к содержанию

Последнее обновление:2026-10-12

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

Сегодня зададим ещё один довольно классический вопрос с собеседования: как функция может вернуть несколько результатов? Сколько способов вы можете предложить?

На самом деле этот вопрос проверяет очень много знаний, он затрагивает:

  • указатели
  • ссылки
  • lambda
  • functional
  • tie
  • tuple
  • struct binding
  • template deduce
  • и так далее

В этой статье мы ответим на этот вопрос разными способами. Сначала преобразуем задачу: будем использовать a / b для моделирования (ниже не рассматривается случай деления на ноль) и возвращать частное и остаток. Проще говоря: сколькими способами функция может вернуть частное и остаток?

Переходим к основному тексту. Примечание: весь код этого раздела впервые опубликован в 知识星球; если интересно, отсканируйте QR-код ниже.

1. Указатели и ссылки

Изменим интерфейс, добавив две переменные — с указателями и ссылками.

void divideWithReferences(int dividend, int divisor, int& quotient, int& remainder) {
  quotient = dividend / divisor;
  remainder = dividend % divisor;
}

void divideWithPointers(int dividend, int divisor, int* quotient, int* remainder) {
  if (quotient) *quotient = dividend / divisor;
  if (remainder) *remainder = dividend % divisor;
}

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

2. Tuple + Tie

Сочетание tuple и tie даёт лаконичный способ возвращать из функции несколько значений. Используя std::tie, мы можем распаковать элементы tuple в разные переменные, повышая ясность кода.

Этот метод можно считать одним из эталонных ответов — он довольно простой и наглядный; если возвращаемых значений два, подойдёт и pair.

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


std::tie(quotient, remainder) = divide(14, 3);
std::cout << quotient << ", " << remainder << std::endl;

3. Struct Binding

Преобразуем предыдущий пример: появившиеся в C++17 структурированные привязки ещё больше упрощают код с возвратом нескольких значений. Использование auto и структурированных привязок делает код более интуитивным и понятным.

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

auto divide(int dividend, int divisor) {
  struct result {
    int quotient;
    int remainder;
  };
  return result{dividend / divisor, dividend % divisor};
}
auto [quotient, remainder] = divide(14, 3);

4. Callback-функция

В повседневных проектах, особенно в открытых проектах, callback — распространённый приём: передавая callback для обработки возвращаемых значений, мы позволяем пользователю самому определять обработку; так и реализуется возврат нескольких значений и более гибкая структура кода. Это очень полезно для асинхронного программирования и обработки событий.

void divide(int dividend, int divisor, std::function<void(int, int)> callback) {
  callback(dividend / divisor, dividend % divisor);
}

5. Собственный Out

Собственный out — это способ с обёрткой в структуру, при котором выходные параметры становятся членами структуры. Такой подход повышает читаемость кода и особенно уместен для функций, которым нужно возвращать несколько значений.

template <class T>
struct out {
  std::function<void(T)> target;

  out(T* t)
      : target([t](T&& in) {
          if (t) *t = std::move(in);
        }) {}

  template <class... Args>
  void emplace(Args&&... args) {
    target(T(std::forward<Args>(args)...));
  }

  template <class X>
  void operator=(X&& x) {
    emplace(std::forward<X>(x));
  }

  template <class... Args>
  void operator()(Args&&... args) {
    emplace(std::forward<Args>(args)...);
  }
};

void divide(int dividend, int divisor, out<int>& quotient_out, out<int>& remainder_out) {
  quotient_out.emplace(dividend / divisor);
  remainder_out.emplace(dividend % divisor);
}

6. Выведение шаблонов

Выведение шаблонов в C++ даёт разработчикам более гибкий и лаконичный способ писать код. Благодаря выведению шаблонов мы можем обрабатывать данные разных типов, не указывая типы явно.

template <typename T1, typename T2>
struct many {
  T1 quotient;
  T2 remainder;
};

template <class T1, class T2>
many(T1, T2) -> many<T1, T2>;

many<int, int> divide(int dividend, int divisor) {
  return many{
      dividend / divisor,
      dividend % divisor,
  };
}

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

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

Похожие темы

Комментарии