Zoeken…


Invoering

Er zijn veel situaties waarin het handig is om meerdere waarden van een functie te retourneren: als u bijvoorbeeld een artikel wilt invoeren en de prijs en het aantal op voorraad wilt retourneren, kan deze functionaliteit nuttig zijn. Er zijn veel manieren om dit in C ++ te doen, en de meeste hebben betrekking op de STL. Als u de STL om een of andere reden echter wilt vermijden, zijn er nog steeds verschillende manieren om dit te doen, waaronder structs/classes en arrays .

Uitvoerparameters gebruiken

Parameters kunnen worden gebruikt voor het retourneren van een of meer waarden; deze parameters moeten niet- const pointers of referenties zijn.

Referenties:

void calculate(int a, int b, int& c, int& d, int& e, int& f) {
    c = a + b;
    d = a - b;
    e = a * b;
    f = a / b;
}

wijzers:

void calculate(int a, int b, int* c, int* d, int* e, int* f) {
    *c = a + b;
    *d = a - b;
    *e = a * b;
    *f = a / b;
}

Sommige bibliotheken of frameworks gebruiken een lege 'OUT' #define om het overduidelijk te maken welke parameters uitvoerparameters zijn in de functiesignatuur. Dit heeft geen functionele impact en zal worden gecompileerd, maar maakt de functiehandtekening een beetje duidelijker;

#define OUT

void calculate(int a, int b, OUT int& c) {
    c = a + b;
}

Met behulp van std :: tuple

C ++ 11

Het type std::tuple kan een willekeurig aantal waarden, mogelijk inclusief waarden van verschillende typen, bundelen in één retourobject:

std::tuple<int, int, int, int> foo(int a, int b) { // or auto (C++14)
   return std::make_tuple(a + b, a - b, a * b, a / b);
}

In C ++ 17 kan een accolades-initialisatielijst worden gebruikt:

C ++ 17
std::tuple<int, int, int, int> foo(int a, int b)    {
    return {a + b, a - b, a * b, a / b};
}

Het ophalen van waarden van de geretourneerde tuple kan omslachtig zijn en vereist het gebruik van de functie std::get template:

auto mrvs = foo(5, 12);
auto add = std::get<0>(mrvs);
auto sub = std::get<1>(mrvs);
auto mul = std::get<2>(mrvs);
auto div = std::get<3>(mrvs);

Als de typen kunnen worden gedeclareerd voordat de functie terugkeert, kan std::tie worden gebruikt om een tuple in bestaande variabelen uit te pakken:

int add, sub, mul, div;
std::tie(add, sub, mul, div) = foo(5, 12);

Als een van de geretourneerde waarden niet nodig is, kan std::ignore worden gebruikt:

std::tie(add, sub, std::ignore, div) = foo(5, 12);
C ++ 17

Gestructureerde bindingen kunnen worden gebruikt om std::tie te voorkomen:

auto [add, sub, mul, div] = foo(5,12);

Als u een tuple van waardeverwijzingen wilt retourneren in plaats van een tuple van waarden, gebruikt u std::tie in plaats van std::make_tuple .

std::tuple<int&, int&> minmax( int& a, int& b ) {
  if (b<a)
    return std::tie(b,a);
  else
    return std::tie(a,b);
}

welke toestaat

void increase_least(int& a, int& b) {
  std::get<0>(minmax(a,b))++;
}

In zeldzame gevallen gebruikt u std::forward_as_tuple plaats van std::tie ; wees voorzichtig als u dit doet, omdat tijdelijke functies mogelijk niet lang genoeg duren om te worden geconsumeerd.

Met behulp van std :: array

C ++ 11

De container std::array kan een vast aantal retourwaarden bundelen. Dit nummer moet tijdens het compileren bekend zijn en alle retourwaarden moeten van hetzelfde type zijn:

std::array<int, 4> bar(int a, int b) {
    return { a + b, a - b, a * b, a / b };
}

Dit vervangt arrays in c-stijl met de vorm int bar[4] . Het voordeel is dat er nu verschillende c++ std-functies op kunnen worden gebruikt. Het biedt ook nuttig lid functies zoals at wat een veilige toegang functie met gebonden controleren, en size waarmee u de grootte van de array terug te keren zonder berekening.

Std :: pair gebruiken

De struct-sjabloon std::pair kan exact twee retourwaarden van twee typen samenvoegen:

#include <utility>
std::pair<int, int> foo(int a, int b) {
    return std::make_pair(a+b, a-b);
}

Met C ++ 11 of hoger kan een initialisatielijst worden gebruikt in plaats van std::make_pair :

C ++ 11
#include <utility>
std::pair<int, int> foo(int a, int b) {
    return {a+b, a-b};
}

De individuele waarden van het geretourneerde std::pair kunnen worden opgehaald met behulp van de first en second lidobjecten van het paar:

std::pair<int, int> mrvs = foo(5, 12);
std::cout << mrvs.first + mrvs.second << std::endl;

Output:

10

Struct gebruiken

Een struct kan worden gebruikt om meerdere retourwaarden te bundelen:

C ++ 11
struct foo_return_type {
    int add;
    int sub;
    int mul;
    int div;
};

foo_return_type foo(int a, int b) {
    return {a + b, a - b, a * b, a / b};
}

auto calc = foo(5, 12);
C ++ 11

In plaats van toewijzing aan afzonderlijke velden, kan een constructor worden gebruikt om het samenstellen van geretourneerde waarden te vereenvoudigen:

struct foo_return_type {
    int add;
    int sub;
    int mul;
    int div;
    foo_return_type(int add, int sub, int mul, int div)
    : add(add), sub(sub), mul(mul), div(div) {}
};

foo_return_type foo(int a, int b) {
     return foo_return_type(a + b, a - b, a * b, a / b);
}

foo_return_type calc = foo(5, 12);

De individuele resultaten die worden geretourneerd door de functie foo() kunnen worden opgehaald door toegang te krijgen tot de lidvariabelen van de struct calc :

std::cout << calc.add << ' ' << calc.sub << ' ' << calc.mul << ' ' << calc.div << '\n';

Output:

17 -7 60 0

Opmerking: Bij gebruik van een struct worden de geretourneerde waarden gegroepeerd in een enkel object en toegankelijk met behulp van betekenisvolle namen. Dit helpt ook om het aantal externe variabelen die in het bereik van de geretourneerde waarden zijn gemaakt, te verminderen.

C ++ 17

Om een struct die uit een functie is geretourneerd uit te pakken, kunnen gestructureerde bindingen worden gebruikt. Dit plaatst de out-parameters op gelijke voet met de in-parameters:

int a=5, b=12;
auto[add, sub, mul, div] = foo(a, b);
std::cout << add << ' ' << sub << ' ' << mul << ' ' << div << '\n';

De uitvoer van deze code is identiek aan die hierboven. De struct wordt nog steeds gebruikt om de waarden van de functie te retourneren. Hierdoor kunt u de velden afzonderlijk behandelen.

Gestructureerde bindingen

C ++ 17

C ++ 17 introduceert gestructureerde bindingen, waardoor het nog eenvoudiger wordt om met meerdere retourtypen om te gaan, omdat u niet hoeft te vertrouwen op std::tie() of handmatig hoeft uit te pakken:

std::map<std::string, int> m;

// insert an element into the map and check if insertion succeeded
auto [iterator, success] = m.insert({"Hello", 42});

if (success) {
    // your code goes here
}

// iterate over all elements without having to use the cryptic 'first' and 'second' names
for (auto const& [key, value] : m) {
    std::cout << "The value for " << key << " is " << value << '\n';
}

Gestructureerde bindingen kunnen standaard worden gebruikt met std::pair , std::tuple en elk type waarvan de niet-statische gegevensleden allemaal openbare directe leden of leden van een ondubbelzinnige basisklasse zijn:

struct A { int x; };
struct B : A { int y; };
B foo();

// with structured bindings
const auto [x, y] = foo();

// equivalent code without structured bindings
const auto result = foo();
auto& x = result.x;
auto& y = result.y;

Als u uw type "tuple-achtig" maakt, werkt het ook automatisch met uw type. Een tuple-achtig is een type met de juiste tuple_size , tuple_element en get geschreven:

namespace my_ns {
    struct my_type {
        int x;
        double d;
        std::string s;
    };
    struct my_type_view {
        my_type* ptr;
    };
}

namespace std {
    template<>
    struct tuple_size<my_ns::my_type_view> : std::integral_constant<std::size_t, 3>
    {};

    template<> struct tuple_element<my_ns::my_type_view, 0>{ using type = int; };
    template<> struct tuple_element<my_ns::my_type_view, 1>{ using type = double; };
    template<> struct tuple_element<my_ns::my_type_view, 2>{ using type = std::string; };
}

namespace my_ns {
    template<std::size_t I>
    decltype(auto) get(my_type_view const& v) {
        if constexpr (I == 0)
            return v.ptr->x;
        else if constexpr (I == 1)
            return v.ptr->d;
        else if constexpr (I == 2)
            return v.ptr->s;
        static_assert(I < 3, "Only 3 elements");
    }
}

nu werkt dit:

my_ns::my_type t{1, 3.14, "hello world"};

my_ns::my_type_view foo() {
    return {&t};
}

int main() {
    auto[x, d, s] = foo();
    std::cout << x << ',' << d << ',' << s << '\n';
}

Een functie gebruiken Object Consument

We kunnen een consument bieden die wordt gebeld met de meerdere relevante waarden:

C ++ 11
template <class F>
void foo(int a, int b, F consumer) {
    consumer(a + b, a - b, a * b, a / b);
}

// use is simple... ignoring some results is possible as well
foo(5, 12, [](int sum, int , int , int ){
    std::cout << "sum is " << sum << '\n';
});

Dit staat bekend als "continuation passing style" .

Je kunt een functie aanpassen die een tuple in een continuerende passerende stijlfunctie verandert via:

C ++ 17
template<class Tuple>
struct continuation {
  Tuple t;
  template<class F>
  decltype(auto) operator->*(F&& f)&&{
    return std::apply( std::forward<F>(f), std::move(t) );
  }
};
std::tuple<int,int,int,int> foo(int a, int b);

continuation(foo(5,12))->*[](int sum, auto&&...) {
  std::cout << "sum is " << sum << '\n';
};

waarbij meer complexe versies beschrijfbaar zijn in C ++ 14 of C ++ 11.

Met behulp van std :: vector

Een std::vector kan nuttig zijn voor het retourneren van een dynamisch aantal variabelen van hetzelfde type. In het volgende voorbeeld wordt int als gegevenstype gebruikt, maar een std::vector kan elk type bevatten dat triviaal kan worden gekopieerd:

#include <vector>
#include <iostream>

// the following function returns all integers between and including 'a' and 'b' in a vector
// (the function can return up to std::vector::max_size elements with the vector, given that
// the system's main memory can hold that many items)
std::vector<int> fillVectorFrom(int a, int b) {
    std::vector<int> temp;
    for (int i = a; i <= b; i++) {
        temp.push_back(i);
    }
    return temp;
}

int main() {    
    // assigns the filled vector created inside the function to the new vector 'v'
    std::vector<int> v = fillVectorFrom(1, 10);

    // prints "1 2 3 4 5 6 7 8 9 10 "
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;
    return 0;
}

Uitvoer Iterator gebruiken

Verschillende waarden van hetzelfde type kunnen worden geretourneerd door een uitvoeriterator aan de functie door te geven. Dit is vooral gebruikelijk voor generieke functies (zoals de algoritmen van de standaardbibliotheek).

Voorbeeld:

template<typename Incrementable, typename OutputIterator>
void generate_sequence(Incrementable from, Incrementable to, OutputIterator output) {
    for (Incrementable k = from; k != to; ++k)
        *output++ = k;
}

Voorbeeld gebruik:

std::vector<int> digits;
generate_sequence(0, 10, std::back_inserter(digits));
// digits now contains {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow