리턴 타입에 의한 함수 과부하?


252

더 많은 정적 유형 언어가 반환 유형별로 함수 / 메소드 오버로드를 지원하지 않는 이유는 무엇입니까? 나는 그렇게 생각하지 않습니다. 매개 변수 유형별로 과부하를 지원하는 것보다 유용하거나 합리적인 것처럼 보이지는 않습니다. 어떻게 그렇게 덜 인기가 있습니까?


답변:


523

다른 반환 형식에 의해 과부하, 무슨 말을 달리 입니다 가능하고 되고 일부 현대 언어에 의해 수행. 일반적인 반대는 코드에서

int func();
string func();
int main() { func(); }

어느 func()것이 호출 되는지 알 수 없습니다 . 몇 가지 방법으로 해결할 수 있습니다.

  1. 그러한 상황에서 어떤 함수가 호출되는지 판별 할 수있는 예측 가능한 방법을 사용하십시오.
  2. 이러한 상황이 발생할 때마다 컴파일 타임 오류입니다. 그러나 프로그래머가 명확하게 할 수있는 구문이 있습니다 (예 :) int main() { (string)func(); }.
  3. 부작용이 없습니다. 부작용이없고 함수의 반환 값을 절대로 사용하지 않으면 컴파일러는 처음에 함수를 호출하지 않아도됩니다.

내가 정기적으로 ( ab ) 두 언어 는 반환 유형에 따라 과부하를 사용합니다 : PerlHaskell . 그들이 무엇을하는지 설명하겠습니다.

Perl 에서는 스칼라리스트 컨텍스트 사이에 근본적인 차이가 있습니다 (다른 것들도 있지만 두 가지가 있다고 가정합니다). Perl의 모든 내장 함수 는 호출되는 컨텍스트 에 따라 다른 작업을 수행 할 수 있습니다 . 예를 들어, join연산자는 목록 컨텍스트 (결합되는 것)를 scalar강제 하는 반면 연산자는 스칼라 컨텍스트를 강제합니다.

print join " ", localtime(); # printed "58 11 2 14 0 109 3 13 0" for me right now
print scalar localtime(); # printed "Wed Jan 14 02:12:44 2009" for me right now.

Perl의 모든 연산자는 스칼라 컨텍스트와 목록 컨텍스트에서 무언가를 수행하며 그림과 같이 다를 수 있습니다. (이것은 단지 임의의 연산자에 대한 것이 아닙니다 localtime. @a리스트 컨텍스트에서 배열을 사용하면 스칼라 컨텍스트에서 배열을 반환하지만 요소 수를 반환하므로 예를 들어 print @a요소를 print 0+@a인쇄하고 크기 를 인쇄합니다. ) 또한 모든 연산자는 컨텍스트를 강제 할 수 있습니다 ( 예 : 추가 +는 스칼라 컨텍스트를 강제합니다). man perlfunc이 문서의 모든 항목에는 이 내용이 기록되어 있습니다. 예를 들어, 여기에 대한 항목 중 일부가 있습니다 glob EXPR.

목록 컨텍스트 EXPR에서 표준 Unix 셸과 같은 값에 대한 파일 이름 확장명 목록 (비어있을 수 있음)을 반환합니다 /bin/csh. 스칼라 컨텍스트에서 glob은 이러한 파일 이름 확장을 반복하여 목록이 소진되면 undef를 반환합니다.

이제리스트와 스칼라 컨텍스트의 관계는 무엇입니까? 음, man perlfunc말한다

다음과 같은 중요한 규칙을 기억하십시오. 목록 컨텍스트의 표현식 동작과 스칼라 컨텍스트의 동작을 연관시키는 규칙은 없으며 그 반대도 마찬가지입니다. 완전히 다른 두 가지 일을 할 수 있습니다. 각 연산자와 함수는 스칼라 컨텍스트에서 반환하는 것이 가장 적합한 값을 결정합니다. 일부 연산자는 목록 컨텍스트에서 반환 된 목록의 길이를 반환합니다. 일부 연산자는 목록에서 첫 번째 값을 반환합니다. 일부 연산자는 목록에서 마지막 값을 반환합니다. 일부 연산자는 성공적인 작업 수를 반환합니다. 일관성을 원치 않는 한 일반적으로 원하는 것을 수행합니다.

따라서 단일 기능을 갖는 것은 간단한 일이 아니며 마지막에 간단한 변환을 수행합니다. 사실, 나는 localtime그런 이유로 그 예를 선택했습니다 .

이 동작을하는 것은 내장 기능 만이 아닙니다. 모든 사용자는을 사용하여 이러한 함수를 정의 할 수 있습니다 wantarray.이를 통해 list, 스칼라 및 void 컨텍스트를 구별 할 수 있습니다. 예를 들어, 빈 공간에서 호출되는 경우 아무것도하지 않기로 결정할 수 있습니다.

지금, 당신은이 아니라고 불평 할 수있다 진실 만이 호출 한 다음 해당 정보에 작용 년대 상황을 이야기 하나 개 기능을 가지고 있기 때문에 반환 값에 의해 오버로드가. 그러나 이것은 분명히 동등합니다 (그리고 Perl이 문자 그대로 일반적인 과부하를 허용하지 않지만 함수가 인수를 검사하는 방법과 유사합니다). 또한이 응답의 시작 부분에 언급 된 모호한 상황을 훌륭하게 해결합니다. Perl은 어떤 메소드를 호출해야할지 모른다고 불평하지 않습니다. 그냥 호출합니다. 함수가 호출 된 컨텍스트를 파악하기 만하면됩니다. 항상 가능합니다.

sub func {
    if( not defined wantarray ) {
        print "void\n";
    } elsif( wantarray ) {
        print "list\n";
    } else {
        print "scalar\n";
    }
}

func(); # prints "void"
() = func(); # prints "list"
0+func(); # prints "scalar"

(참고 : 때때로 기능을 의미 할 때 Perl 연산자라고 말할 수도 있습니다.이 논의에서는 중요하지 않습니다.)

Haskell 은 다른 접근 방식, 즉 부작용이 없습니다. 또한 강력한 유형 시스템을 가지고 있으므로 다음과 같은 코드를 작성할 수 있습니다.

main = do n <- readLn
          print (sqrt n) -- note that this is aligned below the n, if you care to run this

이 코드는 표준 입력에서 부동 소수점 숫자를 읽고 제곱근을 인쇄합니다. 그러나 이것에 대한 놀라운 점은 무엇입니까? 음의 유형은 readLn입니다 readLn :: Read a => IO a. 이것이 의미하는 것은 Read( Read형식 클래스 의 인스턴스 인 모든 형식) 모든 형식에 대해 readLn읽을 수 있다는 것입니다. Haskell은 부동 소수점 숫자를 읽고 싶다는 것을 어떻게 알았습니까? 음, 유형 sqrtIS sqrt :: Floating a => a -> a, 본질적 수단 sqrt만을 입력으로 부동 소수점 숫자에 동의 한 하스켈 내가 원하는 것을 추론 그렇게 할 수 있습니다.

Haskell이 내가 원하는 것을 추론 할 수없는 경우 어떻게됩니까? 글쎄, 몇 가지 가능성이 있습니다. 반환 값을 전혀 사용하지 않으면 Haskell은 단순히 처음에는 함수를 호출하지 않습니다. 나는 그러나, 반환 값을 사용하여 다음 하스켈은 유형을 추론 할 수 없다는 불평 할 것이다 :

main = do n <- readLn
          print n
-- this program results in a compile-time error "Unresolved top-level overloading"

원하는 유형을 지정하여 모호성을 해결할 수 있습니다.

main = do n <- readLn
          print (n::Int)
-- this compiles (and does what I want)

어쨌든,이 전체 토론의 의미는 반환 값으로 과부하가 가능하고 완료되어 귀하의 질문의 일부에 답변한다는 것입니다.

질문의 다른 부분은 왜 더 많은 언어가 그렇게하지 않는가입니다. 다른 사람들이 그 대답을하도록하겠습니다. 그러나 몇 가지 의견은 다음과 같습니다. 주된 이유는 아마도 혼동의 기회가 인수 유형에 의한 과부하보다 실제로 더 클 수 있다는 것입니다. 개별 언어의 근거를 볼 수도 있습니다.

Ada : "가장 간단한 과부하 해결 규칙은 가능한 한 넓은 컨텍스트의 모든 정보를 사용하여 오버로드 된 참조를 해결하는 것입니다.이 규칙은 간단하지만 도움이되지 않습니다. 인간 독자가 필요합니다. 임의로 큰 텍스트 조각을 스캔하고 임의로 복잡한 추론을 만드는 것 (예 : 위의 (g)) 우리는 더 나은 규칙이 인간 독자 나 컴파일러가 수행해야하는 작업을 명시 적으로 만드는 규칙이라고 믿습니다. 인간 독자에게는 가능한 한 자연 스럽습니다. "

C ++ (Bjarne Stroustrup의 "C ++ 프로그래밍 언어"섹션 7.4.1) : "리턴 유형은 과부하 해석에서 고려되지 않습니다. 이유는 개별 연산자 또는 함수 호출의 컨텍스트에 독립적으로 해석을 유지하기위한 것입니다.

float sqrt(float);
double sqrt(double);

void f(double da, float fla)
{
    float fl = sqrt(da);     // call sqrt(double)
    double d = sqrt(da); // call sqrt(double)
    fl = sqrt(fla);            // call sqrt(float)
    d = sqrt(fla);             // call sqrt(float)
}

리턴 유형이 고려되면 더 이상 sqrt()개별 호출을보고 호출 된 함수를 판별 할 수 없습니다. "(비교를 위해 Haskell에서는 암시 적 변환 이 없음을 참고하십시오 .)

Java ( Java Language Specification 9.4.1 ) : "상속 된 메소드 중 하나는 다른 모든 상속 된 메소드에 대해 리턴 유형으로 대체 가능해야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다." (예, 이것이 이론적 근거를 제시하지 않는다는 것을 알고 있습니다. 저는 Gosling이 "Java Programming Language"로 이론적 근거를 제공 할 것이라고 확신합니다. 누군가가 사본을 가지고 있을까요? 그러나 Java에 대한 재미있는 사실 : JVM 반환 값으로 과부하를 허용 합니다! 예를 들어 Scala 에서 사용 되며 내부를 가지고 놀 면서 Java통해 직접 액세스 할 수 있습니다 .

추신. 마지막으로 C ++에서 반환 값을 사용하여 과부하로 실제로 과부하 할 수 있습니다. 증거:

struct func {
    operator string() { return "1";}
    operator int() { return 2; }
};

int main( ) {
    int x    = func(); // calls int version
    string y = func(); // calls string version
    double d = func(); // calls int version
    cout << func() << endl; // calls int version
    func(); // calls neither
}

훌륭한 게시물이지만 독서가 무엇인지 명확히하고 싶을 수도 있습니다 (문자열-> 무언가).
토마스 에딩

C ++에서는 const / not const 반환 값으로 오버로드 할 수도 있습니다. stackoverflow.com/questions/251159/…
geon

3
강제 연산자를 오버로드하는 마지막 트릭의 경우 "cout"행이 작동하지만 코드를 거의 변경하면 " 'operator <<'"에 대한 과부하가 발생합니다.
Steve

1
내가 선호하는 접근 방식은 하나의 과부하가 "선호"로 표시되도록하는 것입니다. 컴파일러는 기본 설정 과부하 만 사용하여 바인딩을 시작한 다음 기본 설정이 아닌 과부하가 개선되는지 확인합니다. 무엇보다도 유형을 지정 Foo하고 Bar양방향 변환을 지원 한다고 가정 하면 메서드는 Foo내부적으로 유형을 사용 하지만 유형을 반환합니다 Bar. 이러한 메소드가 코드에 의해 호출되어 결과를 type으로 즉시 강제 변환 Foo하면 Bar리턴 유형을 사용하는 Foo것이 좋지만 더 나은 방법입니다. BTW, 나는 또한 어떤 수단을보고 싶습니다 ...
supercat

... 메소드 (method)는 구조에서 어떤 유형을 사용해야 하는지를 지정할 수 var someVar = someMethod();있습니다. 예를 들어, 유창함 인터페이스를 변경할 수 불변의 버전을 혜택을 얻을 수있는 구현하는 유형의 가족은, 그래서 var thing2 = thing1.WithX(3).WithY(5).WithZ(9);WithX(3)사본 thing1변경 가능한 객체의 mutate X 및 변경 가능한 객체를 돌려로를; WithY(5)Y를 변경하고 동일한 객체를 반환합니다. 마찬가지로`WithZ (9). 그러면 할당이 불변 유형으로 변환됩니다.
supercat

37

함수가 리턴 유형에 의해 오버로드되고이 두 개의 과부하가 발생한 경우

int func();
string func();

컴파일러가 이와 같은 호출을 볼 때 호출 할 두 함수 중 어떤 것을 알아낼 수있는 방법은 없습니다

void main() 
{
    func();
}

이러한 이유로 언어 디자이너는 종종 반환 값 오버로드를 허용하지 않습니다.

일부 언어 (예 : MSIL 등), 그러나 않는 반환 형식에 의해 오버로드 할 수 있습니다. 물론 위의 어려움에 직면했지만 해결 방법이 있으므로 설명서를 참조해야합니다.


4
사소한 퀴즈 (당신의 대답은 매우 명확하고 이해하기 쉬운 근거를 제공합니다) : 방법이 없다는 것이 아닙니다. 그것은 대부분의 사람들이 원하는 것보다 서투르고 더 고통 스러울뿐입니다. 예를 들어, C ++에서, 추악한 캐스트 구문을 사용하여 과부하를 해결할 수 있었을 것입니다.
Michael Burr

2
@ Jörg W Mittag : 기능이 무엇인지 알 수 없습니다. 그들은 쉽게 다른 부작용을 가질 수 있습니다 .
A. Rex

2
@ Jörg-대부분의 주류 프로그래밍 언어 (C / C ++, C #, Java 등)에서 함수는 일반적으로 부작용이 있습니다. 사실, 부작용이있는 기능이없는 기능은 최소한 공통적이라고 생각합니다.
Michael Burr

6
여기서 늦게 점프하지만 일부 상황에서 "기능"은 "본질적으로"부작용이없는 방법 "의 좁은 정의를 가지고 있습니다. 더 구어 적으로 "기능"은 종종 "방법"또는 "서브 루틴"과 상호 교환 적으로 사용됩니다. Jorg는 귀하의 관점에 따라 엄격하거나
비판적입니다

3
심지어 나중에,보기의 몇 가지 포인트가 엄격한 또는 현학적 이외의 형용사를 사용할 수 있습니다에 점프
패트릭 맥도날드

27

그러한 언어로 다음을 어떻게 해결 하시겠습니까?

f(g(x))

경우 f과부하를했다 void f(int)void f(string)g과부하를 가지고 int g(int)string g(int)? 어떤 종류의 명확성이 필요합니다.

나는 당신이 이것을 필요로하는 상황이 기능의 새로운 이름을 선택함으로써 더 잘 제공 될 것이라고 생각합니다.


2
일반적인 종류의 과부하는 모호함을 초래할 수 있습니다. 필자는 일반적으로 필요한 캐스트 수를 계산하여 해결한다고 생각하지만 항상 작동하지는 않습니다.
Jay Conrod

1
예, 표준 전환은 정확히 일치, 프로모션 및 전환으로 분류됩니다. void f (int); 보이드 f (long); 파'); f (int)를 호출합니다. 이는 프로모션 일 뿐이므로 long으로 변환하는 것은 변환입니다. 공극 f (부동); 무효 f (짧음); f (10); 두 가지 모두에 대한 변환이 필요합니다. 통화가 모호합니다.
Johannes Schaub-litb

언어의 평가가 게으 르면 문제가되지 않습니다.
jdd

Upvote, 매개 변수 유형 오버로드 및 반환 유형 오버로드의 상호 작용은 Rex의 게시물에서 다루지 않습니다. 아주 좋은 지적입니다.
Joseph Garvin

1
언어를 디자인 할 때, 규칙은 오버로드 된 함수에 대해 각 매개 변수 서명이 기본값으로 지정된 하나의 리턴 유형을 가져야한다는 것입니다. 컴파일러는 모든 함수 호출이 기본 유형을 사용한다고 가정하여 시작합니다. 그러나 일단 수행되면 함수의 반환 값이 즉시 캐스팅되거나 다른 것으로 강제되는 모든 상황에서 컴파일러는 매개 변수 서명이 동일하지만 반환 유형이 더 일치하는 (또는 가능한 경우) 오버로드를 확인합니다. . 또한 이러한 오버로드에 대해 "재정의-재정의-재정의"규칙을 적용 할 수도 있습니다.
supercat

19

다른 매우 유사한 질문 (dupe?) 에서 C ++ 특정 답변 을 훔치려면 :


Stroustrup (다른 C ++ 건축가의 입력으로 가정)이 과부하 해상도를 '컨텍스트 독립적'으로 원했기 때문에 단순히 함수 반환 유형이 과부하 해상도에서 작동하지 않습니다. "C ++ 프로그래밍 언어, 제 3 판"의 7.4.1- "오버로드 및 리턴 유형"을 참조하십시오.

그 이유는 개별 연산자 또는 함수 호출의 컨텍스트 독립적 인 해결을 유지하기 위함입니다.

그들은 결과가 어떻게 사용되었는지가 아니라 과부하가 어떻게 호출되었는지에 기반을두기를 원했습니다. 실제로 많은 함수가 결과를 사용하지 않고 호출되거나 결과가 더 큰 표현식의 일부로 사용됩니다. 그들이 결정했을 때 내가 확신하게 된 한 가지 요소는 반환 유형이 해상도의 일부 인 경우 복잡한 규칙으로 해결되거나 컴파일러 던지기가 필요한 오버로드 된 함수에 대한 많은 호출이 있다는 것입니다 호출이 모호하다는 오류.

그리고 주님은 C ++ 과부하 해상도가 충분히 복잡하다는 것을 알고 있습니다.


5

haskell에서는 기능 과부하가 없어도 가능합니다. 하스켈은 타입 클래스를 사용합니다. 프로그램에서 당신은 볼 수 있습니다 :

class Example a where
    example :: Integer -> a

instance Example Integer where  -- example is now implemented for Integer
    example :: Integer -> Integer
    example i = i * 10

함수 과부하 자체는 그렇게 인기가 없습니다. 내가 본 대부분의 언어는 C ++, 아마도 java 및 / 또는 C #입니다. 모든 동적 언어에서는 다음과 같은 축약 형입니다.

define example:i
  ↑i type route:
    Integer = [↑i & 0xff]
    String = [↑i upper]


def example(i):
    if isinstance(i, int):
        return i & 0xff
    elif isinstance(i, str):
        return i.upper()

따라서 아무 의미가 없습니다. 대부분의 사람들은 언어가 당신이 사용하는 곳마다 한 줄씩 떨어 뜨릴 수 있는지에 관심이 없습니다.

패턴 매칭은 함수 오버로딩과 다소 비슷하며 때로는 비슷하게 작동한다고 생각합니다. 비록 소수의 프로그램에만 유용하고 대부분의 언어에서 구현하기 까다로워서 일반적이지 않습니다.

당신은 언어를 구현하기 위해 구현하기 쉬운 다른 많은 기능들이 무한히 많이 있음을 알 수 있습니다.

  • 동적 타이핑
  • 목록, 사전 및 유니 코드 문자열에 대한 내부 지원
  • 최적화 (JIT, 유형 추론, 컴파일)
  • 통합 배포 도구
  • 도서관 지원
  • 지역 사회 지원 및 모임 장소
  • 풍부한 표준 라이브러리
  • 좋은 구문
  • 평가판 인쇄 루프 읽기
  • 반사 프로그래밍 지원

3
하스켈 과부하가 발생했습니다. 형식 클래스는 오버로드 된 함수를 정의하는 데 사용되는 언어 기능입니다.
Lii

2

좋은 답변입니다! A. Rex의 답변은 특히 매우 상세하고 유익합니다. 그가 지적했듯이 C ++ 컴파일 할 때 사용자 제공 유형 변환 연산자를 고려합니다 lhs = func(); (여기서 func는 실제로 구조체의 이름입니다) . 내 해결 방법은 약간 다릅니다-더 나은 것은 아니며 단지 다릅니다 (같은 기본 아이디어를 기반으로하지만).

내가 쓰고 싶었던 반면에 ...

template <typename T> inline T func() { abort(); return T(); }

template <> inline int func()
{ <<special code for int>> }

template <> inline double func()
{ <<special code for double>> }

.. etc, then ..

int x = func(); // ambiguous!
int x = func<int>(); // *also* ambiguous!?  you're just being difficult, g++!

나는 매개 변수가있는 구조체를 사용하는 솔루션으로 끝났습니다 (T = 반환 유형).

template <typename T>
struct func
{
    operator T()
    { abort(); return T(); } 
};

// explicit specializations for supported types
// (any code that includes this header can add more!)

template <> inline
func<int>::operator int()
{ <<special code for int>> }

template <> inline
func<double>::operator double()
{ <<special code for double>> }

.. etc, then ..

int x = func<int>(); // this is OK!
double d = func<double>(); // also OK :)

이 솔루션의 이점은 이러한 템플릿 정의를 포함하는 모든 코드가 더 많은 유형에 대한 전문화를 추가 할 수 있다는 것입니다. 또한 필요에 따라 구조체의 부분 특수화를 수행 할 수 있습니다. 예를 들어 포인터 유형에 대한 특수 처리를 원하는 경우 :

template <typename T>
struct func<T*>
{
    operator T*()
    { <<special handling for T*>> } 
};

부정적으로, 당신은 int x = func();내 솔루션으로 쓸 수 없습니다 . 당신은 작성해야합니다 int x = func<int>();. 컴파일러에게 형식 변환 연산자를보고이를 요청하도록 요청하는 대신 반환 형식이 무엇인지 명시 적으로 말해야합니다. "내"솔루션과 A.Rex는 모두이 C ++ 딜레마를 해결하기위한 파레토 최적화 방식에 속한다고 말합니다. :)


1

반환 유형이 다른 메소드를 오버로드 하려면 오버로드 실행을 허용하도록 더미 매개 변수를 기본값으로 추가하십시오 .

type    
    myclass = class
    public
      function Funct1(dummy: string = EmptyStr): String; overload;
      function Funct1(dummy: Integer = -1): Integer; overload;
    end;

이렇게 사용하세요

procedure tester;
var yourobject : myclass;
  iValue: integer;
  sValue: string;
begin
  yourobject:= myclass.create;
  iValue:= yourobject.Funct1(); //this will call the func with integer result
  sValue:= yourobject.Funct1(); //this will call the func with string result
end;

끔찍한 생각입니다. 더미 매개 변수를 도입하지 마십시오. 코드 냄새가 큽니다. 대신, 다른 이름을 선택하거나 차별적 노동 조합 또는 그와 유사한 행동을하는 반품 유형을 선택하십시오.
Abel

@Abel 당신이 제안하는 것은 실제로 끔찍한 아이디어입니다. 왜냐하면 전체 아이디어 가이 더미 매개 변수에 관한 것이고 개발자 에게이 매개 변수가 더미이며 무시해야한다는 것을 분명히하기 위해 그렇게 명명되었습니다. ... 기본값 더미 매개 변수를 모르는 VCL 델파이, 많은 도서관에서 사용되며, 많은 십오은, 예를 들면 델파이는 SafeLoadLibrary에서 SysUtils 유닛에서 볼 수 있습니다
ZORRO_BLANCO

맵 또는 접기 작업의 람다 또는 인터페이스를 구현할 때와 같이 더미 매개 변수가 유용한 시나리오가 있습니다. 그러나 단순히 과부하를 만들기 위해 나는 동의하지 않습니다. 프로그래머가 없어도 살아갈 필요가없고 소음입니다.
Abel

0

이미 표시된 것처럼 반환 유형에 따라 다른 함수의 모호한 호출은 모호성을 유발합니다. 모호성은 결함있는 코드를 유발합니다. 결함이있는 코드는 피해야합니다.

모호한 시도로 인한 복잡성은 이것이 좋은 해킹이 아님을 보여줍니다. 지적 운동 외에도 참조 매개 변수가있는 절차를 사용하지 않는 것이 좋습니다.

procedure(reference string){};
procedure(reference int){};
string blah;
procedure(blah)

"반환"값을 즉시 재사용 할 수 없기 때문입니다. 반대로 당신은 한 줄에 각 호출을해야 할 것doing(thisVery(deeplyNested(), andOften(butNotAlways()), notReally()), goodCode());
Adowrath

0

이 오버로드 기능은 약간 다른 방식으로 보면 관리하기 어렵지 않습니다. 다음을 고려하세요,

public Integer | String f(int choice){
if(choice==1){
return new string();
}else{
return new Integer();
}}

언어가 오버로드를 반환하면 매개 변수 오버로드는 허용되지만 복제는 허용되지 않습니다. 이것은 다음의 문제를 해결할 것입니다.

main (){
f(x)
}

하나의 f (int choice) 만 선택할 수 있기 때문입니다.


0

.NET에서는 때로는 하나의 매개 변수를 사용하여 일반 결과에서 원하는 출력을 표시 한 다음 예상 한 결과를 얻기 위해 변환했습니다.

씨#

public enum FooReturnType{
        IntType,
        StringType,
        WeaType
    }

    class Wea { 
        public override string ToString()
        {
            return "Wea class";
        }
    }

    public static object Foo(FooReturnType type){
        object result = null;
        if (type == FooReturnType.IntType) 
        {
            /*Int related actions*/
            result = 1;
        }
        else if (type == FooReturnType.StringType)
        {
            /*String related actions*/
            result = "Some important text";
        }
        else if (type == FooReturnType.WeaType)
        {
            /*Wea related actions*/
            result = new Wea();
        }
        return result;
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Expecting Int from Foo: " + Foo(FooReturnType.IntType));
        Console.WriteLine("Expecting String from Foo: " + Foo(FooReturnType.StringType));
        Console.WriteLine("Expecting Wea from Foo: " + Foo(FooReturnType.WeaType));
        Console.Read();
    }

이 예제가 도움이 될 수도 있습니다.

C ++

    #include <iostream>

enum class FooReturnType{ //Only C++11
    IntType,
    StringType,
    WeaType
}_FooReturnType;

class Wea{
public:
    const char* ToString(){
        return "Wea class";
    }
};

void* Foo(FooReturnType type){
    void* result = 0;
    if (type == FooReturnType::IntType) //Only C++11
    {
        /*Int related actions*/
        result = (void*)1;
    }
    else if (type == FooReturnType::StringType) //Only C++11
    {
        /*String related actions*/
        result = (void*)"Some important text";
    }
    else if (type == FooReturnType::WeaType) //Only C++11
    {
        /*Wea related actions*/
        result = (void*)new Wea();
    }
    return result;
}

int main(int argc, char* argv[])
{
    int intReturn = (int)Foo(FooReturnType::IntType);
    const char* stringReturn = (const char*)Foo(FooReturnType::StringType);
    Wea *someWea = static_cast<Wea*>(Foo(FooReturnType::WeaType));
    std::cout << "Expecting Int from Foo: " << intReturn << std::endl;
    std::cout << "Expecting String from Foo: " << stringReturn << std::endl;
    std::cout << "Expecting Wea from Foo: " << someWea->ToString() << std::endl;
    delete someWea; // Don't leak oil!
    return 0;
}

1
일종의 해킹이므로 사용자가 결과를 올바르게 캐스트하지 않거나 개발자가 반환 유형을 열거 형과 올바르게 일치시키지 않으면 런타임 오류가 발생할 수 있습니다. 이 답변
sleblanc

0

레코드의 경우 옥타브 는 반환 요소가 스칼라 대 배열인지에 따라 다른 결과를 허용합니다.

x = min ([1, 3, 0, 2, 0])
   ⇒  x = 0

[x, ix] = min ([1, 3, 0, 2, 0])
   ⇒  x = 0
      ix = 3 (item index)

Cf 또한 특이 값 분해 .


0

이것은 C ++과 약간 다릅니다. 반환 유형으로 직접 과부하로 간주되는지 알 수 없습니다. 그것은 방식으로 작동하는 템플릿 전문화에 가깝습니다.

util.h

#ifndef UTIL_H
#define UTIL_H

#include <string>
#include <sstream>
#include <algorithm>

class util {
public: 
    static int      convertToInt( const std::string& str );
    static unsigned convertToUnsigned( const std::string& str );
    static float    convertToFloat( const std::string& str );
    static double   convertToDouble( const std::string& str );

private:
    util();
    util( const util& c );
    util& operator=( const util& c );

    template<typename T>
    static bool stringToValue( const std::string& str, T* pVal, unsigned numValues );

    template<typename T>
    static T getValue( const std::string& str, std::size_t& remainder );
};

#include "util.inl"

#endif UTIL_H

util.inl

template<typename T>
static bool util::stringToValue( const std::string& str, T* pValue, unsigned numValues ) {
    int numCommas = std::count(str.begin(), str.end(), ',');
    if (numCommas != numValues - 1) {
        return false;
    }

    std::size_t remainder;
    pValue[0] = getValue<T>(str, remainder);

    if (numValues == 1) {
        if (str.size() != remainder) {
            return false;
        }
    }
    else {
        std::size_t offset = remainder;
        if (str.at(offset) != ',') {
            return false;
        }

        unsigned lastIdx = numValues - 1;
        for (unsigned u = 1; u < numValues; ++u) {
            pValue[u] = getValue<T>(str.substr(++offset), remainder);
            offset += remainder;
            if ((u < lastIdx && str.at(offset) != ',') ||
                (u == lastIdx && offset != str.size()))
            {
                return false;
            }
        }
    }
    return true;    
}

util.cpp

#include "util.h"

template<>
int util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoi( str, &remainder );
} 

template<>
unsigned util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoul( str, &remainder );
}

template<>
float util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stof( str, &remainder );
}     

template<>   
double util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stod( str, &remainder );
}

int util::convertToInt( const std::string& str ) {
    int i = 0;
    if ( !stringToValue( str, &i, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
        throw strStream.str();
    }
    return i;
}

unsigned util::convertToUnsigned( const std::string& str ) {
    unsigned u = 0;
    if ( !stringToValue( str, &u, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
        throw strStream.str();
    }
    return u;
}     

float util::convertToFloat(const std::string& str) {
    float f = 0;
    if (!stringToValue(str, &f, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
        throw strStream.str();
    }
    return f;
}

double util::convertToDouble(const std::string& str) {
    float d = 0;
    if (!stringToValue(str, &d, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to double";
        throw strStream.str();
    }
    return d;
}

이 예제는 리턴 유형별로 함수 과부하 분석을 정확하게 사용하지 않지만이 C ++ 비 객체 클래스는 템플리트 특수화를 사용하여 전용 정적 메소드를 사용하여 리턴 유형별로 함수 과부하 분석을 시뮬레이션합니다.

convertToType함수는 함수 템플리트를 호출하고 있으며이 함수 템플리트 stringToValue()의 구현 세부 사항 또는 알고리즘을 보면 호출 getValue<T>( param, param )하고 유형을 리턴 하여 매개 변수 중 하나로 함수 템플리트에 전달 된 유형 T으로 저장합니다. .T*stringToValue()

이와 같은 것 이외의 것; C ++에는 실제로 반환 유형별로 함수 오버로드 해상도를 갖는 메커니즘이 없습니다. 내가 모르는 다른 구문이나 메커니즘이 반환 유형별로 해상도를 시뮬레이션 할 수 있습니다.


-1

나는 이것이 현대 C ++ 정의의 GAP이라고 생각합니다 ... 왜 그렇습니까?

int func();
double func();

// example 1. → defined
int i = func();

// example 2. → defined
double d = func();

// example 3. → NOT defined. error
void main() 
{
    func();
}

C ++ 컴파일러가 "3"예제에서 오류를 발생시키지 않고 "1 + 2"예제에서 코드를 수락 할 수없는 이유는 무엇입니까 ??


예, 그것이 C # (그리고 아마도 C ++)에 대해 고려했던 것입니다. 그러나 코드가 사소한 반면 클래스 계층, 가상 메서드, 초록 및 인터페이스, 다른 오버로드 및 때로는 여러 상속을 추가하면 어떤 메서드를 해결해야하는지 결정하는 것이 매우 복잡해집니다. 그 길을 가지 않는 것은 디자이너의 선택이지만 다른 언어는 다양한 성공 단계에서 다르게 결정되었습니다.
Abel

-2

대부분의 정적 언어는 이제 제네릭을 지원하므로 문제를 해결할 수 있습니다. 앞에서 언급했듯이 매개 변수 diff가 없으면 어느 것을 호출할지 알 수있는 방법이 없습니다. 따라서이 작업을 수행하려면 제네릭을 사용하고 하루 만 호출하십시오.


같은 것이 아닙니다. 입력을 정수, float, bool 또는 반환 유형이 사용되는 방식에 따라 다른 것으로 변환하는 함수를 어떻게 처리합니까? 각각 특별한 경우가 필요하기 때문에 일반화 할 수 없습니다.
Jay Conrod

"반환 유형 오버로드"에 대한 영리한 전략 은 codeproject.com/KB/cpp/returnoverload.aspx 를 참조하십시오 . 기본적으로 func () 함수를 정의하는 대신 구조체 func를 정의하고 operator () () 및 각 적절한 유형으로 변환하십시오.
j_random_hacker

Jay, 함수를 호출 할 때 리턴 유형을 정의하십시오. inpus가 다르면 전혀 문제가 없습니다. 동일하면 GetType ()을 사용하여 유형에 따라 일부 논리를 가질 수있는 일반 버전을 가질 수 있습니다.
Charles Graham
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.