std::pair и std::tuple в C++: как возвращать несколько значений из функции
Поисковый запрос, под который написана статья: как вернуть несколько значений из функции C++. Это частая задача: функция должна вернуть, например, минимальное и максимальное число, результат операции и сообщение об ошибке, координаты точки или несколько рассчитанных параметров. В C++ для этого удобно использовать std::pair, std::tuple и структурные привязки, появившиеся в C++17.
Почему нельзя просто вернуть несколько значений через return?
Оператор return возвращает одно значение. Но это одно значение может быть составным: объектом, структурой, парой или кортежем. Плохой вариант — использовать глобальные переменные или передавать всё через неконстантные ссылки без необходимости. Такой код сложнее читать и тестировать.
std::pair: когда нужно вернуть два значения
std::pair — это простой контейнер ровно для двух значений. Он находится в заголовке <utility>. Типы значений могут быть разными: например, int и std::string.
#include <iostream>
#include <utility>
#include <vector>
std::pair<int, int> findMinMax(const std::vector<int>& numbers) {
int minValue = numbers[0];
int maxValue = numbers[0];
for (int x : numbers) {
if (x < minValue) minValue = x;
if (x > maxValue) maxValue = x;
}
return {minValue, maxValue};
}
int main() {
std::vector<int> values = {7, 2, 10, 4};
auto result = findMinMax(values);
std::cout << "Минимум: " << result.first << "\n";
std::cout << "Максимум: " << result.second << "\n";
}
У std::pair поля называются first и second. Это удобно для коротких примеров, но в большом коде такие имена не всегда понятны. Если пара означает «минимум и максимум», читателю приходится помнить порядок значений.
Structured bindings: аккуратная распаковка пары
Начиная с C++17, результат можно распаковать в переменные с понятными именами. Это делает код значительно читабельнее.
auto [minValue, maxValue] = findMinMax(values);
std::cout << minValue << " " << maxValue << "\n";
Такой подход особенно хорош для функций, которые возвращают небольшой набор связанных значений. Главное — не злоупотреблять: если значений много или у них сложный смысл, лучше создать отдельную структуру с именованными полями.
std::tuple: если значений больше двух
std::tuple похож на std::pair, но может хранить три, четыре и больше значений. Он находится в заголовке <tuple>. Например, функция парсинга может вернуть статус, число и текст ошибки.
#include <iostream>
#include <string>
#include <tuple>
std::tuple<bool, int, std::string> parseNumber(const std::string& text) {
try {
int value = std::stoi(text);
return {true, value, ""};
} catch (...) {
return {false, 0, "Не удалось преобразовать строку в число"};
}
}
int main() {
auto [ok, value, error] = parseNumber("123");
if (ok) {
std::cout << "Число: " << value << "\n";
} else {
std::cout << "Ошибка: " << error << "\n";
}
}
Без structured bindings пришлось бы использовать std::get<0>(result), std::get<1>(result) и так далее. Это работает, но читается хуже, особенно если порядок элементов неочевиден.
Когда лучше использовать struct вместо pair или tuple
std::pair и std::tuple удобны, но не всегда являются лучшим выбором. Если возвращаемые данные имеют важный предметный смысл, лучше объявить структуру. Имена полей будут документировать код сами по себе.
#include <string>
struct ParseResult {
bool success;
int value;
std::string errorMessage;
};
ParseResult parseNumberBetter(const std::string& text) {
try {
return {true, std::stoi(text), ""};
} catch (...) {
return {false, 0, "Некорректное число"};
}
}
Правило простое: pair используйте для двух очевидно связанных значений, tuple — для коротких технических результатов, struct — для данных, которые важны для бизнес-логики и будут использоваться в разных местах программы.
Практический пример: деление с проверкой ошибки
Допустим, нужно выполнить деление и вернуть не только результат, но и информацию об успехе операции. Для учебного примера подойдёт std::pair<bool, double>.
#include <iostream>
#include <utility>
std::pair<bool, double> safeDivide(double a, double b) {
if (b == 0.0) {
return {false, 0.0};
}
return {true, a / b};
}
int main() {
auto [success, result] = safeDivide(10.0, 2.0);
if (success) {
std::cout << "Результат: " << result << "\n";
} else {
std::cout << "Ошибка: деление на ноль\n";
}
}
В реальном проекте для отсутствующего результата часто лучше использовать std::optional или отдельный тип результата, но для понимания идеи возврата нескольких значений этот пример отлично подходит.
Типичные ошибки новичков
- Путать порядок элементов. В
tupleлегко забыть, что лежит на позиции 0, 1 или 2. Если смысл неочевиден — создайтеstruct. - Возвращать слишком большой tuple. Кортеж из пяти-семи элементов почти всегда хуже именованной структуры.
- Использовать pair ради всего подряд.
firstиsecondне объясняют предметную область. - Игнорировать пустые входные данные. В примере с минимумом и максимумом нужно отдельно обработать пустой вектор, если такая ситуация возможна.
Рекомендации
- Для C++17 и новее используйте
auto [a, b] = function();— это короче и понятнее. - Не бойтесь возвращать объекты по значению: современные компиляторы хорошо оптимизируют такие возвраты.
- Если результат функции — важная сущность, дайте ей имя через
struct. - Подключайте только нужные заголовки:
<utility>дляpair,<tuple>дляtuple.
Если вы хотите системно разобраться не только с pair, tuple и функциями, но и с фундаментом языка, практикой ООП, STL и современным C++, обратите внимание на пошаговый курс «Программирование на C++ с Нуля до Гуру». Он хорошо подходит тем, кто хочет учиться последовательно, а не собирать знания из случайных фрагментов.
Вывод
Чтобы вернуть несколько значений из функции C++, используйте std::pair для двух значений, std::tuple для небольшого набора значений и struct, когда данным нужны понятные имена. В современном C++ связка pair/tuple + structured bindings позволяет писать компактный, безопасный и читаемый код без лишних обходных решений.
-
Создано 12.08.2026 17:05:09
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.