Programing

C ++ 함수에서 여러 값 반환

lottogame 2020. 4. 27. 07:56
반응형

C ++ 함수에서 여러 값 반환


C ++ 함수에서 여러 값을 반환하는 선호되는 방법이 있습니까? 예를 들어, 두 정수를 나누고 몫과 나머지를 모두 반환하는 함수를 상상해보십시오. 내가 일반적으로 보는 한 가지 방법은 참조 매개 변수를 사용하는 것입니다.

void divide(int dividend, int divisor, int& quotient, int& remainder);

변형은 하나의 값을 반환하고 다른 하나는 참조 매개 변수를 통해 전달하는 것입니다.

int divide(int dividend, int divisor, int& remainder);

다른 방법은 모든 결과를 포함하도록 구조체를 선언하고 다음을 반환하는 것입니다.

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

이러한 방법 중 하나가 일반적으로 선호되거나 다른 제안이 있습니까?

편집 : 실제 코드에서 두 개 이상의 결과가있을 수 있습니다. 또한 유형이 다를 수 있습니다.


두 개의 값을 반환하기 위해 std::pair(보통 typedef'd)를 사용합니다. 두 개 이상의 반환 결과를 보려면 boost::tuple(C ++ 11 이상에서는 std::tuple)을 살펴보십시오 .

C ++ 17에 구조적 바인딩을 도입하면 반환 std::tuple이 표준으로 받아 들여질 것입니다.


C ++ 11에서는 다음을 수행 할 수 있습니다.

#include <tuple>

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

#include <iostream>

int main() {
    using namespace std;

    int quotient, remainder;

    tie(quotient, remainder) = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

C ++ 17에서 :

#include <tuple>

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

#include <iostream>

int main() {
    using namespace std;

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

    cout << quotient << ',' << remainder << endl;
}

또는 구조체와 함께 :

auto divide(int dividend, int divisor) {
    struct result {int quotient; int remainder;};
    return result {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto result = divide(14, 3);

    cout << result.quotient << ',' << result.remainder << endl;

    // or

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

    cout << quotient << ',' << remainder << endl;
}

개인적으로 나는 일반적으로 여러 가지 이유로 반환 매개 변수를 싫어합니다.

  • 어떤 매개 변수가 있고 어떤 것이 호출인지 항상 명확하지는 않습니다.
  • 일반적으로 결과를 잡기 위해 로컬 변수를 만들어야하지만 반환 값은 인라인으로 사용할 수 있습니다 (좋은 아이디어 일 수도 있고 아닐 수도 있지만 적어도 옵션이 있습니다)
  • 함수에 "in door"와 "out door"가있는 것이 더 깨끗해 보입니다. 모든 입력이 여기로 들어가고 모든 출력이 여기로 나옵니다.
  • 논증 목록을 가능한 한 짧게 유지하고 싶습니다

또한 쌍 / 튜플 기술에 대한 예약이 있습니다. 주로 반환 값에 대한 자연스러운 순서가없는 경우가 많습니다. 코드 리더는 result.first가 몫인지 나머지인지 어떻게 알 수 있습니까? 구현자는 순서를 변경하여 기존 코드를 손상시킬 수 있습니다. 컴파일러 오류 또는 경고가 생성되지 않도록 값이 동일한 유형 인 경우 특히 교묘합니다. 실제로 이러한 인수는 매개 변수를 반환하는 데에도 적용됩니다.

다음은 또 다른 코드 예제입니다.

pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth,
                                               double planeAirspeed, double planeCourse);

pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.first << endl;
cout << result.second << endl;

이것은 지상 속도와 코스 또는 코스와 지상 속도를 인쇄합니까? 분명하지 않습니다.

이것과 비교하십시오 :

struct Velocity {
    double speed;
    double azimuth;
};
Velocity calculateResultingVelocity(double windSpeed, double windAzimuth,
                                    double planeAirspeed, double planeCourse);

Velocity result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.speed << endl;
cout << result.azimuth << endl;

나는 이것이 더 분명하다고 생각합니다.

그래서 나는 일반적으로 첫 번째 선택은 구조체 기술이라고 생각합니다. 쌍 / 튜플 아이디어는 특정 경우에 훌륭한 솔루션 일 것입니다. 가능한 경우 반환 매개 변수를 피하고 싶습니다.


std::pair<int, int> divide(int dividend, int divisor)
{
   // :
   return std::make_pair(quotient, remainder);
}

std::pair<int, int> answer = divide(5,2);
 // answer.first == quotient
 // answer.second == remainder

std :: pair는 본질적으로 struct 솔루션이지만 이미 정의되어 있으며 두 가지 데이터 유형에 맞게 조정할 수 있습니다.


실제 기능과 여러 값의 의미 및 크기에 전적으로 의존합니다.

  • 그것들이 분수 예제와 관련이 있다면 구조체 또는 클래스 인스턴스와 함께 갈 것입니다.
  • 그것들이 실제로 관련이 없으며 클래스 / 구조체로 그룹화 할 수 없다면 메소드를 두 가지로 리팩토링해야합니다.
  • 반환하는 값의 메모리 내 크기에 따라 클래스 인스턴스 또는 구조체에 대한 포인터를 반환하거나 참조 매개 변수를 사용할 수 있습니다.

이를위한 OO 솔루션은 비율 클래스를 작성하는 것입니다. 추가 코드 (일부 저장)가 필요하지 않으며 훨씬 깨끗하고 명확 하며이 클래스 외부에서 코드를 정리할 수있는 추가 리팩토링을 제공합니다.

실제로 누군가가 구조를 반환하는 것이 좋습니다.이 구조는 충분히 가깝지만 생성자와 몇 가지 방법, 실제로는 원래 언급 한 "메소드"가있는 완전히 생각하는 클래스가되어야한다는 의도를 숨 깁니다 ( pair) 자신의 인스턴스를 반환하는이 클래스의 멤버 일 가능성이 높습니다.

나는 당신의 예제가 단지 "예"라는 것을 알고 있지만 사실 함수가 어떤 함수보다 더 많은 기능을 수행하지 않는 한 여러 값을 반환하기를 원한다면 거의 확실하게 객체가 누락되어 있습니다.

OO의 마법 인 작은 작업을 수행하기 위해이 작은 클래스를 만드는 것을 두려워하지 마십시오. 모든 방법이 매우 작고 단순하며 모든 클래스가 작고 이해할 수있을 때까지 분해합니다.

OO에는 본질적으로 데이터가 없습니다 .OO는 데이터를 전달하는 것이 아니며 클래스는 내부적으로 자체 데이터를 관리하고 조작해야합니다 (액세서 ​​포함) 무언가를 다시 생각해야 할 수도 있다는 표시입니다.


의 C 구조체를 리턴 (따라서 C ++)이 표준에 대한 선례가 div, ldiv(C99, 그리고 lldiv부터) 함수 <stdlib.h>(또는 <cstdlib>).

'반환 값과 반환 매개 변수의 혼합'은 일반적으로 가장 깨끗하지 않습니다.

함수가 리턴 매개 변수를 통해 상태를 리턴하고 데이터를 리턴하게하는 것은 C에서 의미가 있습니다. C ++에서는 예외를 사용하여 실패 정보를 대신 릴레이 할 수있는 분명하지 않습니다.

반환 값이 두 개 이상인 경우 구조와 유사한 메커니즘이 가장 좋습니다.


C ++ 17을 사용하면 하나 이상의 이동 불가능 / 복사 불가능한 값을 반환 할 수도 있습니다 (특정 경우). 움직일 수없는 유형을 반환 할 수있는 가능성은 새로운 보장 된 반환 값 최적화를 통해 제공되며 집계템플릿 생성자 라고하는 항목으로 구성 됩니다.

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

이것에 대한 예쁜 점은 복사 또는 이동을 유발 하지 않는다는 것 입니다. 예제 manystruct를 variadic으로 만들 수도 있습니다. 자세한 내용은:

C ++ 17 variadic template 'construction deduction guide'에 대한 variadic 집계 (struct)와 구문 반환


반환 값에 구조체 또는 클래스를 사용하십시오. std::pair지금은 사용 이 가능하지만

  1. 나중에 더 많은 정보를 원한다고 결정하면 융통성이 없습니다.
  2. 반환되는 내용과 순서는 헤더의 함수 선언에서 명확하지 않습니다.

자체 문서화 멤버 변수 이름을 가진 구조를 반환하면 함수를 사용하는 사람에게는 버그가 덜 발생할 수 있습니다. 동료 모자를 잠시 착용하면 divide_result기능의 잠재적 사용자 인 2 초 후에 즉시 구조를 쉽게 이해할 수 있습니다. 출력 매개 변수 또는 신비한 쌍과 튜플을 사용하면 많은 시간이 걸리고 잘못 사용될 수 있습니다. 그리고 아마도 함수를 몇 번 사용한 후에도 여전히 올바른 인수 순서를 기억하지 못할 것입니다.


함수가 참조를 통해 값을 반환하는 경우 이론적으로 첫 번째 함수는 전역 적으로 액세스 가능한 변수에 전달 된 변수의 주소를 저장할 수 있고, 하위 적으로 호출되는 함수는 다른 함수를 호출 할 때 컴파일러가이를 레지스터에 저장할 수 없습니다. 컴파일러는 (1) 다른 함수를 호출하기 전에 레지스터의 값을 메모리에 다시 저장하고 (2) 이러한 호출 후 메모리에서 다시 필요할 때 다시 읽습니다.

참조로 돌아 오면 프로그램 최적화가 어려워집니다


여기에 C ++에서 여러 값 (두 개 이상의 값)을 반환하는 프로그램을 작성 중입니다. 이 프로그램은 c ++ 14 (G ++ 4.9.2)에서 실행 가능합니다. 프로그램은 계산기와 같습니다.

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

따라서이 방법으로 함수에서 여러 값을 반환 할 수 있음을 명확하게 이해할 수 있습니다. std :: pair를 사용하면 2 개의 값만 반환되고 std :: tuple은 3 개 이상의 값을 반환 할 수 있습니다.


여러 매개 변수를 반환하는 방법에는 여러 가지가 있습니다. 나는 광대 할 것이다.

참조 매개 변수를 사용하십시오.

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

포인터 매개 변수를 사용하십시오.

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

&호출 사이트에서 호출 해야한다는 이점이 있습니다 .

템플릿을 작성하여 사용하십시오.

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ *t = 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>
  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 foo( out<int> result, out<int> other_result )

그리고 모두 좋다. foo더 이상 보너스로 전달 된 값을 읽을 수 없습니다.

데이터를 넣을 수있는 지점을 정의하는 다른 방법을 사용하여 구성 할 수 있습니다 out. 예를 들어 어딘가에 물건을 배치하는 콜백.

구조체를 반환 할 수 있습니다 :

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

whick works ok in every version of C++, and in 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 :

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

as well. Prior to 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 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.


I tend to use out-vals in functions like this, because I stick to the paradigm of a function returning success/error codes and I like to keep things uniform.


Alternatives include arrays, generators, and inversion of control, but none is appropriate here.

Some (e.g. Microsoft in historical Win32) tend to use reference parameters for simplicity, because it's clear who allocates and how it will look on the stack, reduces the proliferation of structures, and allows a separate return value for success.

"Pure" programmers prefer the struct, assuming it is the function value (as is the case here), rather than something that's touched incidentally by the function. If you had a more complicated procedure, or something with state, you'd probably use references (assuming you have a reason for not using a class).


I'd say there is no preferred method, it all depends on what you're going to do with the response. If the results are going to be used together in further processing then structures make sense, if not I'd tend to pass then as individual references unless the function was going to be used in a composite statement:

x = divide( x, y, z ) + divide( a, b, c );

I often choose to pass 'out structures' by reference in the parameter list rather than having the pass by copy overhead of returning a new structure (but this is sweating the small stuff).

void divide(int dividend, int divisor, Answer &ans)

Are out parameters confusing? A parameter sent as reference suggests the value is going to change (as opposed to a const reference). Sensible naming also removes confusion.


Why do you insist on a function with multiple return values? With OOP you can use a class offering a regular function with a single return value, and any number of additional "return values" like below. The advantage is that the caller has a choice of looking at the extra data members, but is not required to do this. This is the preferred method for complicated data base or networking calls, where lots of additional return info may be needed in case errors occur.

To answer your original question, this example has a method to return the quotient, which is what most callers may need, and additionally, after the method call, you can get the remainder as a data member.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};

rather than returning multiple values,just return one of them and make a reference of others in the required function for eg:

int divide(int a,int b,int quo,int &rem)

Boost tuple would be my preferred choice for a generalized system of returning more than one value from a function.

Possible example:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}

We can declare the function such that, it returns a structure type user defined variable or a pointer to it . And by the property of a structure, we know that a structure in C can hold multiple values of asymmetrical types (i.e. one int variable, four char variables, two float variables and so on…)


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.

참고URL : https://stackoverflow.com/questions/321068/returning-multiple-values-from-a-c-function

반응형