Последнее обновление: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++ предоставляет множество способов реализовать возврат нескольких значений, и у каждого своя сфера применения. Выбор подходящего способа зависит от конкретных требований; разработчики могут гибко использовать эти возможности, исходя из структуры кода и читаемости.
Похожие темы¶
- C++ чисто виртуальные функции и абстрактные классы | C++那些事
- C++ утверждения assert | C++那些事
- C++ битовые поля | C++那些事
- Реализация полиморфизма C++ на языке C | C++那些事
- C++ константы const и его модификация | C++那些事
- C++ всё семейство const | C++那些事
- C++ оператор разыменования * | C++那些事
- C++ инициализация и присваивание | C++那些事
- C++ ключевое слово mutable | C++那些事
- C++ restrict и оптимизации компилятора | C++那些事