Returning multiple values from a C++ function

后端 未结 21 2504
别跟我提以往
别跟我提以往 2020-11-22 01:04

Is there a preferred way to return multiple values from a C++ function? For example, imagine a function that divides two integers and returns both the quotient and the rema

相关标签:
21条回答
  • 2020-11-22 01:49

    Here, i am writing a program that is returning multiple values(more than two values) in c++. This program is executable in c++14 (G++4.9.2). program is like a calculator.

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

    So, you can clearly understand that in this way you can return multiple values from a function. using std::pair only 2 values can be returned while std::tuple can return more than two values.

    0 讨论(0)
  • 2020-11-22 01:50

    For returning two values I use a std::pair (usually typedef'd). You should look at boost::tuple (in C++11 and newer, there's std::tuple) for more than two return results.

    With introduction of structured binding in C++ 17, returning std::tuple should probably become accepted standard.

    0 讨论(0)
  • 2020-11-22 01:50

    There is precedent for returning structures in the C (and hence C++) standard with the div, ldiv (and, in C99, lldiv) functions from <stdlib.h> (or <cstdlib>).

    The 'mix of return value and return parameters' is usually the least clean.

    Having a function return a status and return data via return parameters is sensible in C; it is less obviously sensible in C++ where you could use exceptions to relay failure information instead.

    If there are more than two return values, then a structure-like mechanism is probably best.

    0 讨论(0)
  • 2020-11-22 01:50

    I would just do it by reference if it's only a few return values but for more complex types you can also just do it like this :

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

    use "static" to limit the scope of the return type to this compilation unit if it's only meant to be a temporary return type.

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

    This is definitely not the prettiest way to do it but it will work.

    0 讨论(0)
  • 2020-11-22 01:51

    There are a bunch of ways to return multiple parameters. I'm going to be exhastive.

    Use reference parameters:

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

    use pointer parameters:

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

    which has the advantage that you have to do a & at the call-site, possibly alerting people it is an out-parameter.

    Write a template and use it:

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

    then we can do:

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

    and all is good. foo is no longer able to read any value passed in as a bonus.

    Other ways of defining a spot you can put data can be used to construct out. A callback to emplace things somewhere, for example.

    We can return a structure:

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

    whick works ok in every version of C++, and in c++17 this also permits:

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

    at zero cost. Parameters can even not even be moved thanks to guaranteed elision.

    We could return a std::tuple:

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

    which has the downside that parameters are not named. This permits the c++17:

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

    as well. Prior to c++17 we can instead do:

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

    which is just a bit more awkward. Guaranteed elision doesn't work here, however.

    Going into stranger territory (and this is after out<>!), we can use continuation passing style:

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

    and now callers do:

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

    a benefit of this style is you can return an arbitrary number of values (with uniform type) without having to manage memory:

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

    the value callback could be called 500 times when you get_all_values( [&](int value){} ).

    For pure insanity, you could even use a continuation on the continuation.

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

    whose use looks like:

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

    which would permit many-one relationships between result and other.

    Again with uniforn values, we can do this:

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

    here, we call the callback with a span of results. We can even do this repeatedly.

    Using this, you can have a function that efficiently passes megabytes of data without doing any allocation off the stack.

    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
    }
    

    Now, std::function is a bit heavy for this, as we would be doing this in zero-overhead no-allocation environments. So we'd want a function_view that never allocates.

    Another solution is:

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

    where instead of taking the callback and invoking it, foo instead returns a function which takes the callback.

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

    this breaks the output parameters from the input parameters by having separate brackets.

    With variant and c++20 coroutines, you could make foo a generator of a variant of the return types (or just the return type). The syntax is not yet fixed, so I won't give examples.

    In the world of signals and slots, a function that exposes a set of signals:

    template<class...Args>
    struct broadcaster;
    
    broadcaster<int, int> foo();
    

    allows you to create a foo that does work async and broadcasts the result when it is finished.

    Down this line we have a variety of pipeline techniques, where a function doesn't do something but rather arranges for data to be connected in some way, and the doing is relatively independant.

    foo( int_source )( int_dest1, int_dest2 );
    

    then this code doesn't do anything until int_source has integers to provide it. When it does, int_dest1 and int_dest2 start recieving the results.

    0 讨论(0)
  • 2020-11-22 01:54

    With C++17 you can also return one ore more unmovable/uncopyable values (in certain cases). The possibility to return unmovable types come via the new guaranteed return value optimization, and it composes nicely with aggregates, and what can be called templated constructors.

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

    The pretty thing about this is that it is guaranteed to not cause any copying or moving. You can make the example many struct variadic too. More details:

    Returning variadic aggregates (struct) and syntax for C++17 variadic template 'construction deduction guide'

    0 讨论(0)
提交回复
热议问题