기능 과부하? 예 또는 아니오 [닫힘]


16

정적으로 강력하고 형식이 지정된 컴파일 언어를 개발 중이며 언어 오버로드 기능을 언어 기능으로 포함할지 여부를 다시 생각하고 있습니다. 나는 주로 C[++|#]배경 에서 비롯되어 약간 편향되어 있음을 깨달았습니다 .

가장 설득력있는 인수 무엇 을위한에 대한 언어로 오버로드 기능을 포함하여?


편집 : 반대 의견을 가진 사람이 없습니까?

: 버트 랜드 마이어 (1985/1986에서 에펠 다시의 창조자) 방법이 오버로드를 호출 (소스)

OO 언어의 의미 론적 힘에는 아무런 영향을 미치지 않지만 가독성을 저해하고 모든 사람의 작업을 복잡하게하는 허영 메커니즘

이제는 일부 일반화이지만 똑똑한 사람이므로 필요할 경우 백업 할 수 있다고 말하는 것이 안전하다고 생각합니다. 실제로 그는 거의 CLSv1 개발자 중 한 명인 Brad Abrams에게 .NET이 메소드 오버로드를 지원하지 않아야한다고 확신했습니다. (출처) 그것은 강력한 것입니다. 누구든지 자신의 생각과 25 년이 지난 후에도 그의 견해가 정당화되는지 여부를 밝힐 수 있습니까?

답변:


24

함수 오버로딩은 C ++ 스타일 템플릿 코드에 매우 중요합니다. 다른 유형에 대해 다른 함수 이름을 사용해야하는 경우 일반 코드를 작성할 수 없습니다. 그것은 C ++ 라이브러리의 많은 부분과 C ++ 기능의 대부분을 제거 할 것입니다.

일반적으로 멤버 함수 이름에 존재합니다. A.foo()와는 완전히 다른 함수를 호출 할 수 B.foo()있지만 두 함수는 모두 이름이 지정 foo됩니다. +정수 및 부동 소수점 숫자에 적용될 때 다른 것들 과 마찬가지로 연산자에 존재하며 종종 문자열 연결 연산자로 사용됩니다. 정규 기능에서도 허용하지 않는 것이 이상합니다.

호출되는 정확한 기능은 두 가지 데이터 유형에 따라 달라지는 공통 Lisp 스타일 "멀티 메소드"를 사용할 수 있습니다. Common Lisp Object System에서 프로그래밍하지 않은 경우 이것을 쓸모 없게 만들기 전에 시도하십시오. C ++ 스트림에 필수적입니다.

함수 과부하가없는 I / O (또는 더 나쁜 가변 함수)는 다른 유형의 값을 인쇄하거나 다른 유형의 값을 공통 유형 (문자열)으로 변환하기 위해 여러 가지 다른 기능이 필요합니다.

함수 과부하가 없으면 변수 또는 값의 유형을 변경하면 변수를 사용하는 모든 함수를 변경해야합니다. 코드를 리팩토링하기가 훨씬 어렵습니다.

사용자가 사용중인 형식 명명 규칙을 기억할 필요가없고 표준 함수 이름 만 기억할 때 API를보다 쉽게 ​​사용할 수 있습니다.

연산자 오버로드가 없으면 기본 작업을 둘 이상의 유형에서 사용할 수있는 경우 사용하는 유형으로 각 기능에 레이블을 지정해야합니다. 이것은 본질적으로 헝가리어 표기법이며, 나쁜 방법입니다.

전반적으로 언어를 훨씬 더 유용하게 만듭니다.


1
+1, 모두 아주 좋은 포인트. 그리고 다중 방법이 쓸모 없다고 생각하지 않습니다 ... 방문자 패턴을 사용해야 할 때마다 입력하는 바로 키보드를 저주합니다.
자기 소개-이름을 생각하십시오

이 사람이 함수를 오버라이드하지 않고 오버라이드하지 않고 설명하고 있지 않습니까?
dragosb

8

적어도 Haskell의 타입 클래스를 알고있는 것이 좋습니다. 타입 클래스는 연산자 오버로딩에 대한 훈련 된 접근 방식으로 만들어졌지만, 다른 용도를 발견하고 어느 정도까지 Haskell을 만들었습니다.

예를 들어, 다음은 애드혹 오버로드의 예입니다 (유효하지 않은 Haskell).

(==) :: Int -> Int -> Bool
x == y = ...
x /= y = not (x == y)

(==) :: Char -> Char -> Bool
x == y = ...
x /= y = not (x == y)

그리고 타입 클래스로 오버로드하는 것과 같은 예가 있습니다 :

class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool

    x /= y  =  not (x == y)

instance Eq Int where
    x == y  = ...

instance Eq Char where
    x == y  = ...

이것의 단점은 당신의 모든 typeclasses에 대한 펑키 이름을 마련 할 필요가 있다는 것이다 (하스켈처럼, 당신은 오히려 추상적이 Monad, Functor, Applicative뿐만 아니라 간단하고 인식 Eq, NumOrd).

단점은 일단 typeclass에 익숙해지면 해당 클래스에서 모든 유형을 사용하는 방법을 알고 있다는 것입니다. 또한 다음과 같이 필요한 클래스를 구현하지 않는 유형에서 함수를 쉽게 보호 할 수 있습니다.

group :: (Eq a) => [a] -> [[a]]
group = groupBy (==)

편집 : Haskell에서 ==두 가지 다른 유형을 허용 하는 연산자를 원하면 다중 매개 변수 유형 클래스를 사용할 수 있습니다.

class Eq a b where
    (==) :: a -> b -> Bool
    (/=) :: a -> b -> Bool

    x /= y  =  not (x == y)

instance Eq Int Int where
    x == y  = ...

instance Eq Char Char where
    x == y  = ...

instance Eq Int Float where
    x == y  = ...

물론 이것은 사과와 오렌지를 명시 적으로 비교할 수 있기 때문에 아마도 나쁜 생각 일 것입니다. 그러나 일부 상황 에서는 실제로 +a Word8를 추가 Int하는 것이 현명한 일 이므로 이것을 고려할 수도 있습니다.


+1,이 개념을 처음 읽은 이후로이 개념을 파악하려고 노력해 왔으며, 이것이 도움이됩니다. 이 패러다임이 예를 들어 (==) :: Int -> Float -> Bool어디에서나 정의 할 수 없습니까? (물론 그것이 좋은 아이디어인지 여부에 관계없이)
자기 자신에 대한 참고-이름을 생각하십시오.

다중 매개 변수 유형 클래스 (Haskell이 확장으로 지원하는)를 허용하면 가능합니다. 예를 들어 답변을 업데이트했습니다.
Joey Adams

흠, 흥미로운. 따라서 기본적 class Eq a ...으로 pseudo-C-family로 변환되고 interface Eq<A> {bool operator==(A x, A y);}템플릿 코드를 사용하여 임의의 객체를 비교하는 대신이 '인터페이스'를 사용합니다. 맞습니까?
자기 소개-이름을 생각하십시오.

권리. Go의 인터페이스를 빠르게 살펴볼 수도 있습니다. 즉, 인터페이스를 구현하는 것으로 유형을 선언 할 필요가 없으며 해당 인터페이스에 대한 모든 메소드를 구현하면됩니다.
Joey Adams

1
@ Notetoself-thinkofaname : 추가 연산자에 관하여-예 및 아니오. 것이 허용 ==다른 이름 공간에 있어야하지만, 그것을 대체 할 수 없습니다. 단일 네임 스페이스 (떨어져 있음을 유의하시기 바랍니다 Prelude) 기본적으로 포함하지만 확장을 사용하거나 명시 적으로 가져 와서 그것을로드 방지 할 수는 ( import Prelude ()에서 아무것도를 가져올 것 Preludeimport qualified Prelude as P하지 않습니다 현재 이름 공간에 기호를 삽입).
Maciej Piechotka

4

기능 과부하를 허용하면 선택적 매개 변수로 다음을 수행 할 수 없습니다 (또는 가능하지 않은 경우).

사소한 예는 방법이 없다고 가정base.ToString()

string ToString(int i) {}
string ToString(double d) {}
string ToString(DateTime d) {}
...

강력하게 입력 된 언어의 경우 예. 약한 유형의 언어는 아닙니다. 약한 유형의 언어에서 하나의 기능만으로 위의 작업을 수행 할 수 있습니다.
spex

2

항상 함수 오버로드보다 기본 매개 변수를 선호했습니다. 오버로드 된 함수는 일반적으로 기본 매개 변수를 사용하여 "기본"버전을 호출합니다. 왜 쓰는가

int indexOf(char ch)
{
  return self.indexOf(ch, 0);
}

int indexOf(char ch, int fromIndex)
{
  // Do whatever
}

내가 할 수있을 때 :

int indexOf(char ch, int fromIndex=0)
{
  // Do whatever
}

즉, 때로는 오버로드 된 함수가 기본 매개 변수를 사용하여 다른 변형을 호출하는 대신 다른 일을한다는 것을 알고 있습니다. 그러나 그러한 경우에는 그냥 좋은 아이디어가 아닙니다. 다른 이름.

또한 파이썬 스타일 키워드 인수는 기본 매개 변수와 잘 작동합니다.


좋아, 다시 해보자, 내가 무엇에 대해 ...이 시간을 감지 할 수 있도록 노력하겠습니다 Array Slice(int start, int length) {...}오버로드 Array Slice(int start) {return this.Slice(start, this.Count - start);}? 기본 매개 변수를 사용하여 코딩 할 수 없습니다. 다른 이름을 부여해야한다고 생각하십니까? 그렇다면 어떻게 이름을 지정 하시겠습니까?
자기 소개

모든 오버로드 사용을 다루지는 않습니다.
MetalMikester

@MetalMikester : 내가 놓친 느낌은 무엇입니까?
mipadi

@mipadi 목록에서 indexOf(char ch)+ 와 같은 것을 놓칩니다 indexOf(Date dt). 나는 기본값도 좋아하지만 정적 입력과 호환되지 않습니다.
Mark

2

방금 Java를 설명했습니다. 또는 C #.

왜 바퀴를 재발 명합니까?

리턴 유형이 메소드 서명의 일부이고 하트 컨텐츠에 과부하 인지 확인하십시오 . 말할 필요가 없을 때 실제로 코드를 정리합니다.

function getThisFirstWay(int type)
{ ... }
function getThisSecondWay(int type, double limit)
{ ... }
function getThisThirdWay(int type, String match)
{ ... }

7
내가 알고있는 모든 언어에서 반환 유형이 메서드 서명의 일부가 아니거나 과부하 해결에 사용할 수있는 부분이 아닌 이유가 있습니다. 변수 나 속성에 결과를 할당하지 않고 함수를 프로 시저로 호출 할 때 컴파일러는 다른 모든 인수가 동일한 경우 호출 할 버전을 어떻게 파악해야합니까?
메이슨 휠러

@Mason : 반환 될 것으로 예상되는 반환 유형을 발견 할 수는 있지만 그렇게 할 것으로 기대하지는 않습니다.
Josh K

1
음 ... 당신의 휴리스틱은 당신이 어떤 리턴 값을 기대하지 않을 때 무엇이 ​​리턴 될지 어떻게 알 수 있습니까?
메이슨 휠러

1
타입 기대 휴리스틱은 실제로 제자리에 있습니다 EnumX.Flag1 | Flag2 | Flag3. 그래도 나는 이것을 구현하지 않을 것이다. 내가하고 반환 유형을 사용하지 않은 경우 반환 유형을 찾습니다 void.
자기 소개-이름을 생각하십시오

1
@ Mason : 좋은 질문이지만,이 경우 void 함수를 언급 할 것입니다. 또한 이론적으로 는 모두 동일한 기능을 수행하고 단순히 다른 형식으로 데이터를 반환하기 때문에 둘 중 하나를 선택할 수 있습니다.
Josh K

2

Grrr .. 아직 언급 할 권한이 없습니다 ..

@Mason Wheeler : 반환 유형에 과부하가 걸리는 Ada에주의하십시오. 또한 내 언어 Felix는 일부 컨텍스트에서, 특히 함수가 다른 함수를 반환하고 다음과 같은 호출이있을 때 그것을 수행합니다.

f a b  // application is left assoc: (f a) b

b의 유형은 과부하 해결에 사용될 수 있습니다. 또한 어떤 상황에서는 C ++이 반환 유형에서 오버로드됩니다.

int (*f)(int) = g; // choses g based on type, not just signature

실제로 유형 유추를 사용하여 반환 유형에 오버로드하는 알고리즘이 있습니다. 실제로 기계를 사용하는 것은 어렵지 않습니다. 문제는 인간이 기계를 찾는다는 것입니다. (나는 개요가 Dragon Book에 있다고 생각합니다. 알고리즘은 올바르게 기억하면 시소 알고리즘이라고합니다)


2

함수 오버로딩 구현에 대한 유스 케이스 : 같은 방식으로 동작하지만 다양한 패턴으로 완전히 다른 인수 세트를 사용하는 25 개의 유사한 메소드.

함수 오버로딩을 구현하지 않는 유스 케이스 : 정확히 동일한 패턴으로 매우 유사한 유형의 세트를 가진 5 개의 유사한 이름의 메소드.

하루가 끝나면 두 경우 모두 생성 된 API에 대한 문서를 기대하지 않습니다.

그러나 어떤 경우에는 사용자가 무엇을 할 수 있는지에 관한 것입니다. 다른 경우에는 언어 제한으로 인해 사용자가해야 할 일입니다. IMO는 최소한 프로그램 작성자가 모호함을 만들지 않고 현명하게 과부하 될 수있는 가능성을 허용하는 것이 좋습니다. 당신이 그들의 손을 때리고 선택권을 빼앗을 때 당신은 기본적으로 모호함이 일어날 것이라고 보장하고 있습니다. 나는 항상 잘못된 일을 할 것이라고 가정하는 것보다 올바른 일을하도록 사용자를 신뢰하는 것에 관한 것입니다. 내 경험상, 보호주의는 언어 공동체의 행동을 더욱 악화시키는 경향이있다.


1

필자는 필자의 언어로 일반적인 오버로드 및 다중 유형 유형 클래스를 제공하기로 결정했습니다.

나는 특히 많은 숫자 유형 (Felix는 모든 C의 숫자 유형을 가짐) 인 언어에서 오버로드가 필수적이라고 생각합니다. 그러나 템플릿을 사용하여 오버로드를 악용하는 C ++과 달리 Felix 다형성은 매개 변수입니다. C ++의 템플릿은 잘못 설계되었으므로 C ++의 템플릿에 대해 오버로드가 필요합니다.

유형 클래스도 Felix에서 제공됩니다. C ++을 알고 있지만 Haskell을 이해하지 못하는 사람들에게는 C ++을 오버로드로 묘사하는 사람들을 무시하십시오. 오버로드와 같은 것이 아니라 템플릿 전문화와 비슷합니다. 구현하지 않은 템플릿을 선언 한 다음 필요에 따라 특정 사례에 대한 구현을 제공합니다. 타이핑은 파라 메트릭 방식으로 다형성이며, 구현은 임시 인스턴스화에 의한 것이지만 제한되지는 않습니다. 의도 된 의미를 구현해야합니다.

Haskell (및 C ++)에서는 의미를 설명 할 수 없습니다. C ++에서 "개념"아이디어는 대략 시맨틱을 근사하려는 시도입니다. Felix에서는 공리, 축소, 명예 및 정리로 의도를 근사 할 수 있습니다.

주 및 단지 펠릭스처럼 잘 원칙 언어로 오버로드 (개방)의 장점은 프로그램 라이터 및 코드 검토를 위해 모두 쉽게 라이브러리 함수 이름을 기억 할 수 있다는 것입니다.

오버로드의 주요 단점은이를 구현하는 데 필요한 복잡한 알고리즘입니다. 또한 형식 유추와 잘 어울리지 않습니다. 두 가지가 완전히 배타적이지는 않지만 두 가지를 수행하는 알고리즘은 프로그래머가 아마도 결과를 예측할 수 없을 정도로 복잡합니다.

C ++에서는 조잡한 일치 알고리즘이 있고 자동 유형 변환도 지원하기 때문에 문제가됩니다. Felix에서는 자동 유형 변환없이 정확한 일치를 요구하여이 문제를 "고정"했습니다.

그래서 당신은 내가 생각하는 선택이 있습니다 : 과부하 또는 타입 추론. 추론은 귀엽지 만 충돌을 올바르게 진단하는 방식으로 구현하기도 매우 어렵습니다. 예를 들어, Ocaml은 충돌이 감지되는 위치를 알려 주지만 예상되는 유형을 유추 한 위치는 알려주지 않습니다.

오버로드는 모든 후보를 알려주는 양질의 컴파일러가 있더라도 후보자가 다형성인지 읽기가 어려울 수 있으며 C ++ 템플릿 해커 인 경우 더 나빠질 수 있습니다.


흥미로운 것 같습니다. 자세한 내용을 읽고 싶지만 Felix 웹 페이지의 문서에 대한 링크가 손상되었습니다.
자기 소개 –

예, 전체 사이트가 현재 건설 중입니다 (다시). 죄송합니다.
Yttrill

0

상황에 따라 달라 지지만 다른 사람이 작성한 것을 사용하면 오버로드가 클래스를 훨씬 유용하게 생각합니다. 종종 중복성이 줄어 듭니다.


0

C 계열 언어에 익숙한 사용자를 원한다면 사용자가 기대하기 때문에 그래야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.