연산자 오버로드에 대한 주장을 이해하지 못합니다.


82

나는 조엘의 기사 중 하나를 읽었습니다 .

일반적으로, 나는 물건을 숨기는 언어 기능이 약간 무섭다는 것을 인정 해야 합니다. 코드가 보이면

i = j * 5;

… C에서는 적어도 j에 5가 곱해지고 i에 저장된 결과가 있다는 것을 알고 있습니다.

그러나 C ++에서 동일한 코드 스 니펫을 보면 아무것도 모릅니다. 아무것도. C ++에서 실제로 무슨 일이 일어나고 있는지 알 수있는 유일한 방법은 i와 j가 어떤 타입인지 알아내는 것입니다. j는 operator*오버로드 된 유형 일 수 있으며 곱하려고 할 때 정말 재치있는 일입니다.

(Emphasis mine.) 사물을 숨기는 언어 기능이 무섭습니까? 당신은 어떻게 그것을 두려워 할 수 있습니까? 객체 지향 프로그래밍의 주요 아이디어 중 하나 ( 추상 이라고도 함)를 숨기지 않습니까? 메소드를 호출 할 때마다 a.foo(b)그 기능이 무엇인지 전혀 모릅니다. 어떤 유형 a과 유형 , b다른 곳에서 선언 될 수 있는 유형을 찾아야합니다 . 프로그래머에게 너무 많은 것을 숨기므로 객체 지향 프로그래밍을 제거해야합니까?

그리고 어떻게 j * 5에서 어떤 다른 j.multiply(5)당신이 연산자 오버로딩을 지원하지 않는 언어로 작성해야 할 수있는? lo, 보라, 재치있는 무언가를 하는 메소드 가있는 유형 일 수 있기 때문에 메소드 j내부 의 유형 과 엿보기 를 다시 찾아야합니다 .multiplyjmultiply

"Muahaha, 나는 방법의 이름을 짓는 사악한 프로그래머 multiply이지만, 실제로하는 것은 완전히 애매하고 비 직관적이며, 곱셈과는 전혀 관계가 없습니다." 프로그래밍 언어를 디자인 할 때 고려해야 할 시나리오입니까? 그런 다음 오해의 소지가있을 수 있으므로 프로그래밍 언어의 식별자를 포기해야합니다!

메소드의 기능을 알고 싶다면 문서를 보거나 구현 내부를 들여다 볼 수 있습니다. 연산자 과부하는 단지 구문 설탕이며, 그것이 게임을 어떻게 변화시키는 지 보지 못합니다.

제발 깨달아 줘


20
+1 : 잘 쓰여지고 논쟁이 잘되고 흥미로운 주제이며 논쟁의 여지가 있습니다. p.se 질문의 빛나는 예.
Allon Guralnek

19
+1 : 사람들은 Joel Spolsky가 잘 쓰고 잘 알려져 있기 때문에 들었습니다. 그러나 이것이 그를 100 % 제대로하지는 않습니다. 나는 당신의 주장에 동의합니다. 우리가 여기서 Joel의 논리를 따랐다면, 우리는 아무데도 가지 않을 것입니다.
아무도

5
나는 i와 j가 로컬로 선언되어 있으므로 유형을 빨리 볼 수 있거나 짜증나는 변수 이름이므로 적절하게 이름을 바꿔야한다고 주장합니다.
Cameron MacFarland

5
+1. 그러나 Joel의 기사에서 가장 중요한 부분을 잊지 마십시오. 정답을 향해 마라톤을 완주 한 후에는 명백한 이유없이 50 피트를 멈 춥니 다. 잘못된 코드는 잘못 보이면 안됩니다. 컴파일해서는 안됩니다.
Larry Coleman

3
@Larry : 클래스를 적절하게 정의하여 잘못된 코드를 컴파일하지 못하게 할 수 있으므로 그의 예에서는 C ++에 SafeString 및 UnsafeString이 있거나 RowIndex 및 ColumnIndex가있을 수 있지만 직관적으로 작동하려면 연산자 오버로드를 사용해야합니다.
David Thornley

답변:


32

추상화는 코드를 숨겨서 내부 작업에 대해 걱정할 필요가 없으며 종종 변경할 수는 없지만 코드를 보지 못하게하려는 의도는 아닙니다. 우리는 운영자에 대한 가정을하고 Joel이 말했듯이 어디에나있을 수 있습니다. 특정 위치에 모든 과부하 된 운영자를 설정 해야하는 프로그래밍 기능이 있으면 그것을 찾는 데 도움이 될 수 있지만 더 쉽게 사용할 수 있는지 확실하지 않습니다.

데이터를 삭제하는 Get_Some_Data라는 함수보다 곱셈과 닮지 않는 무언가를 만드는 것이 보이지 않습니다.


13
+1 '보지 못합니다'비트. 언어 기능은 남용이 아니라 사용할 수 있습니다.
Michael K

5
그러나 우리는 <<C ++의 표준 라이브러리에서 비트 단위 시프트와 관련이없는 스트림에 연산자를 정의했습니다.
Malcolm

'비트 단위 이동'연산자는 역사적인 이유로 만 호출됩니다. 표준 유형에 적용하면 (+ 연산자가 숫자 유형에 적용될 때 숫자를 함께 추가하는 것과 같은 방식으로) 비트 단위 이동을 수행하지만 복잡한 유형에 적용하면 원하는대로 원하는대로 수행 할 수 있습니다. 그 유형에 대한 감각.
gbjbaanb

1
스마트 포인터와 반복자가 수행하는 역 참조에도 사용됩니다. 좋고 나쁜 오버로드 사이의 경계를 넣어 곳이 분명하지 않다
martinkunev

그것은 아무데도 없으며 j의 유형 정의에있을 것입니다.
Andy

19

IMHO, 연산자 과부하와 같은 언어 기능은 프로그래머에게 더 많은 힘을줍니다. 그리고 우리 모두 알다시피, 큰 힘으로 큰 책임이 따릅니다. 더 많은 힘을주는 기능은 또한 발로 자신을 쏠 수있는 더 많은 방법을 제공하며, 분명히 신중하게 사용해야합니다.

예를 들어 +또는에 대한 *연산자 class Matrix또는 연산자에 과부하가 걸리는 것이 좋습니다 class Complex. 모든 사람이 그 의미를 즉시 알 수 있습니다. 반면에, +Java가 언어의 일부로 이것을 수행하고 STL이 std::string연산자 오버로드 를 사용 하더라도 문자열 연결 을 의미 한다는 사실 은 전혀 분명하지 않습니다 .

연산자 오버로딩이 코드를 더 명확하게 만드는 또 다른 좋은 예는 C ++의 스마트 포인터입니다. 스마트 포인터가 가능한 한 일반적인 포인터처럼 작동하기를 원하므로 단항 *->연산자 에 과부하가 걸리는 것이 좋습니다 .

본질적으로 연산자 오버로딩은 함수의 이름을 지정하는 또 다른 방법 일뿐입니다. 함수의 이름을 지정하는 규칙이 있습니다. 이름은 설명 적이어야하며 함수의 기능을 즉시 알 수 있어야합니다. 연산자 오버로드에도 동일한 규칙이 적용됩니다.


1
마지막 두 문장은 연산자 오버로드에 대한 이의 제기의 핵심입니다. 즉, 모든 코드가 즉시 명확 해지기를 원합니다.
Larry Coleman

2
M과 N이 Matrix 타입 인 M * N의 의미가 명확하지 않습니까?
Dima

2
@ 프레드 : 아니요. 한 종류의 행렬 곱셈이 있습니다. mxn 행렬에 nxk 행렬을 곱하여 mxk 행렬을 얻을 수 있습니다.
Dima

1
@FredOverflow : 3 차원 벡터를 곱하는 방법에는 여러 가지가 있습니다. 하나는 스칼라를 제공하고 다른 하나는 3 차원 벡터를 제공하므로 *혼동을 유발할 수 있습니다. 아마도 operator*()닷 제품과 operator%()크로스 제품에 사용할 수는 있지만 일반적인 라이브러리에는 사용하지 않을 것입니다.
David Thornley

2
베켓 @Martin : 번호의 C ++은 순서에 허가되어 있지 않은 A-B등의 B-A중, 모든 사업자는 그 패턴을 따른다. 항상 예외는 하나 있습니다 : 컴파일러가 중요하지 않다는 것을 증명할 수 있으면 모든 것을 재정렬 할 수 있습니다.
Sjoerd

9

Haskell "+", "-", "*", "/"등은 단지 (중위) 기능입니다.

"4 plus 2"에서와 같이 삽입 기능의 이름을 "plus"로 지정해야합니까? 추가가 함수가하는 것이라면 어떨까요? "plus"기능의 이름을 "+"로 지정해야합니까? 왜 안돼?

소위 "연산자"의 문제는 대부분 수학 연산과 유사하며 해석 할 방법이 많지 않기 때문에 이러한 방법 / 함수 / 연산자가 무엇을하는지에 대한 기대가 높다고 생각합니다.

편집 : 내 요지를 더 명확하게 만들었습니다.


C에서 상속받은 것을 제외하고는 C ++ (그리고 Fred가 요구 한 것)도 거의 똑같습니다. 이제 이것이 좋은지 나쁜지 무엇을 취합니까?
sbi

@sbi 나는 사실도 C가 과부하했다 운영자가 ... 당신은 그들을 사용할 수 있습니다 ... 연산자 오버로딩 사랑 int, float, long long무엇이든지. 그게 다 뭐에요?
FUZxxl

@FUZxxl : 이것은 내장 연산자를 오버로드하는 사용자 정의 연산자 에 관한 것입니다.
sbi

1
@sbi Haskell은 내장사용자 정의를 구분하지 않습니다 . 모든 연산자가 동일합니다. 미리 정의 된 내용을 모두 제거하고 연산자를 포함하여 처음부터 모든 것을 작성할 수 있도록 일부 확장 기능을 켤 수도 있습니다.
FUZxxl

@FUZxxl : 그럴 수도 있지만, 오버로드 된 연산자 +는 다른 내장 숫자 유형 에 대해 내장 기능을 사용하는 것이 아니라 사용자 정의 과부하를 생성하는 데 반대 합니다. 따라서 내 의견.
sbi

7

내가 본 다른 답변을 바탕으로 운영자 과부하에 대한 실제 이의 제기는 즉각적인 명백한 코드에 대한 요구라고 결론 지을 수 있습니다.

이것은 두 가지 이유로 비극적입니다.

  1. 논리적 인 결론에 따르면, 코드가 분명해야한다는 원칙은 우리 모두가 여전히 COBOL로 코딩하게 할 것입니다.
  2. 즉시 명백한 코드를 배우지 않습니다. 작동 방식에 대해 생각하는 데 시간이 걸리면 이해하기 쉬운 코드를 통해 배웁니다.

코드에서 배우는 것이 항상 주요 목표는 아닙니다. "feature X가 고장 나고, 그것을 쓴 사람이 회사를 떠났으 며 최대한 빨리 수정해야합니다"와 같은 경우에는 즉시 명백한 코드를 갖고있을 것입니다.
Errorsatz

5

다소 동의합니다.

당신이 작성하는 경우 multiply(j,5), j제작, 스칼라 또는 매트릭스 타입이 될 수 multiply()무엇에 따라 다소 복잡 j하다. 그러나 오버로드라는 아이디어를 완전히 버린다면 함수의 이름을 지정 multiply_scalar()하거나 multiply_matrix()그 아래에서 무슨 일이 일어나고 있는지 분명히 알 수 있습니다.

우리 중 많은 사람들이 편도를 선호하는 코드가 있고 대부분의 사람들이 다른 방향을 선호하는 코드가 있습니다. 그러나 대부분의 코드는이 두 극단 사이의 중간 지점에 속합니다. 당신이 선호하는 것은 당신의 배경과 개인적 선호에 달려 있습니다.


좋은 지적. 그러나 오버로드를 모두 버리는 것은 일반적인 프로그래밍으로는 훌륭하지 않습니다.
fredoverflow

@FredO : 물론 아닙니다. 그러나 일반 프로그래밍은 매우 다른 유형에 대해 동일한 알고리즘을 사용하는 multiply_matrix()것이므로 일반 프로그래밍을 선호하는 사람들 은 좋아하지 않습니다.
sbi

2
당신은 이름에 대해 오히려 낙관적입니까? 내가 일한 일부 장소를 기준으로 'multiply ()`및'multiplym ()`과 같은 이름 real_multiply()이 필요합니다. 개발자들은 종종 이름이 좋지 않으며 operator*()최소한 일관성이 있어야합니다.
David Thornley

@David : 예, 이름이 나쁘다는 사실을 건너 뛰었습니다. 그러나 우리는 operator*()어리석은 일 을 할 수 있다고 가정 할 수 j있으며 5 개의 함수 호출과 관련된 표현식을 평가하는 매크로입니다. 그러면 더 이상 두 가지 접근 방식을 비교할 수 없습니다. 그러나 시간이 걸리더라도 가치가 있지만, 이름을 잘 지정하는 것은 어렵습니다.
sbi

5
@David : 이름을 짓기가 어렵 기 때문에 이름은 프로그래밍 언어에서 추방되어야합니다. 잘못하기가 너무 쉽습니다! ;-)
fredoverflow

4

연산자 오버로드에 두 가지 문제가 있습니다.

  1. 과부하는 프로그래머가 의도하지 않은 경우에도 연산자의 의미를 변경합니다. 예를 들어, 과부하 때 &&, ||또는 ,, 당신은 이러한 연산자의 내장 변종에 의해 암시하는 시퀀스 포인트 (뿐만 아니라 논리 연산자의 단락 동작을) 잃게됩니다. 이러한 이유로 언어가 허용하더라도 이러한 연산자에 과부하를주지 않는 것이 좋습니다.
  2. 어떤 사람들은 연산자 오버로딩이 훌륭한 기능이라고 생각하며, 적절한 솔루션이 아니더라도 어디서나 사용하기 시작합니다. 이로 인해 다른 사람들이 다른 방향으로 과도하게 반응하여 작업자 과부하를 사용하지 않도록 경고합니다. 나는 어느 그룹에도 동의하지 않지만 중간 입장을 취하십시오 : 운영자 과부하는 약간만 사용해야합니다.
    • 과부하 된 운영자는 도메인 전문가와 소프트웨어 전문가 모두에게 자연스러운 의미를 갖습니다. 이 두 그룹이 운영자의 자연스러운 의미에 동의하지 않으면 과부하하지 마십시오.
    • 관련된 유형 (들)에 대해, 연산자에 대한 자연스러운 의미는 없으며 즉각적인 맥락 (바람직하게는 같은 표현이지만 몇 줄을 넘지 않음)은 항상 연산자의 의미가 무엇인지 명확하게합니다. 이 범주의 예는 operator<<스트림입니다.

1
나에게서 +1이지만 두 번째 인수는 상속에도 동일하게 적용될 수 있습니다. 많은 사람들은 상속에 대한 단서가 없으며 모든 것에 적용하려고합니다. 나는 대부분의 프로그래머들이 상속을 오용하는 것이 가능하다는 데 동의 할 것이라고 생각한다. 그것은 상속이 "악"이며 프로그래밍 언어에서 버려 져야한다는 것을 의미합니까? 아니면 유용 할 수 있기 때문에 그대로 두어야합니까?
fredoverflow

@FredOverflow 두 번째 인수는 "새롭고 인기있는"항목에 적용 할 수 있습니다. 언어에서 연산자 오버로드를 제거한다는 주장으로 제시하지는 않지만 사람들이 언어를 반대하는 이유로 사용합니다. 내가 아는 한, 운영자 과부하는 유용하지만 조심스럽게 사용해야합니다.
Bart van Ingen Schenau

IMHO의 과부하 허용 &&하고 ||순서를 의미하지 않는 방법으로하는 것은 (C ++는 사람들의 오버로드를 허용 할 예정인지 IMHO는, 그것이 첫 번째 기능이 요구되면서, 특별한 "두 기능"형식을 사용해야 큰 실수를했다 암시 적으로 정수로 변환 가능한 형식을 반환하려면 두 번째 함수는 두 번째 또는 세 개의 인수를 사용할 수 있으며 두 번째 함수의 "extra"인수는 첫 번째 함수의 반환 형식입니다. 컴파일러는 첫 번째 함수를 호출 한 다음, 0이 아닌 값을 반환하면 두 번째 피연산자를 평가하고 두 번째 피연산자를 호출합니다.)
supercat

물론 쉼표 연산자에 과부하가 걸리는 것만 큼 기괴하지는 않습니다. 부수적으로, 내가 실제로 보지 않았지만 좋아하는 과부하가 걸리는 것 중 하나는 멤버에 대한 노출을 요구하지 않고 클래스에 foo.bar[3].X의해 처리되는 것과 같은 식을 바인딩하는 멤버 바인딩의 수단이 될 것 입니다. 아래 첨자를 지원 한 다음 멤버를 노출시킬 수 있습니다. 실제 회원 액세스를 통해 평가를 강제하고 싶다면을 쓰십시오 . foofooX((foo.bar)[3]).X
supercat

3

필자의 개인적인 경험을 바탕으로 여러 메소드를 허용하지만 연산자를 오버로드하지 않는 Java 방식은 운영자를 볼 때마다 정확히 무엇을 하는지를 의미합니다.

*이상한 코드를 호출 하는지 여부를 알 필요 는 없지만 코드가 곱셈임을 알고 Java 언어 사양에 정의 된 방식과 동일하게 동작합니다. 이것은 프로그래머가 정의한 모든 개찰구를 찾는 대신 실제 행동에 집중할 수 있음을 의미합니다.

즉, 연산자 오버로드를 금지하는 것은에 혜택입니다 독자 가 아닌 작가 , 따라서 유지 보수 프로그램을 쉽게!


+1,주의 사항 : C ++은 자신을 걸기에 충분한 로프를 제공합니다. 그러나 C ++에서 링크 된 목록을 구현하려면 []를 사용하여 n 번째 요소에 액세스하는 기능을 원합니다. 연산자 (수학적으로 말하면)가 유효한 데이터에 연산자를 사용하는 것이 좋습니다.
Michael K

@ 마이클, list.get(n)구문으로 살 수 없습니까?

@ Thorbjørn : 실제로 좋지만, 아마도 좋지 않은 예일 것입니다. 오버로드 +,-시간이 더 나을 수도 있습니다. 시간보다는 의미가 있습니다 .add (anotherTime).
Michael K

4
@Michael : 연결된 목록에 std::list대해서는 오버로드 operator[](또는 목록에 색인을 생성하는 다른 방법을 제공) 하지 않습니다 . 왜냐하면 그러한 작업은 O (n) 일 것이므로 효율성에 관심이 있으면 목록 인터페이스가 그러한 기능을 노출해서는 안됩니다. 클라이언트는 인덱스를 사용하여 연결된 목록을 반복하여 유혹하여 O (n) 알고리즘을 불필요하게 O (n ^ 2)로 만들 수 있습니다. 특히 사람들이 List복잡성을 완전히 추상화하려는 인터페이스로 작업하는 경우 Java 코드에서 매우 자주 나타납니다 .
fredoverflow

5
@Thor : "하지만 반드시 확인해야합니다 :)"... 다시 말하지만, 이것은 연산자 오버로딩과 관련이 없습니다 . 이 보이면 time.add(anotherTime)라이브러리 프로그래머가 추가 작업을 "올바르게"구현했는지 확인해야합니다 (무엇이든).
fredoverflow

3

오버로드 a * b와 호출의 한 가지 차이점 multiply(a,b)은 후자를 쉽게 잡을 수 있다는 것입니다. multiply함수가 다른 유형에 대해 오버로드되지 않은 경우 a및 의 유형을 추적하지 않고도 함수가 수행 할 작업을 정확하게 찾을 수 있습니다 b.

Linus Torvalds는 연산자 오버로드에 대한 흥미로운 주장을 가지고 있습니다. 대부분의 변경 사항이 전자 메일을 통해 패치를 통해 전송되는 Linux 커널 개발과 같이 관리자는 각 변경에 대한 몇 줄의 컨텍스트만으로 패치가 수행 할 작업을 이해할 수 있어야합니다. 함수와 연산자에 과부하가 걸리지 않으면 패치를 컨텍스트 독립적 인 방식으로 더 쉽게 읽을 수 있습니다. 변경된 파일을 통해 모든 유형이 무엇인지 알아 내고 과부하 된 연산자를 확인할 필요가 없기 때문입니다.


리눅스 커널이 순수 C로 개발되지 않았습니까? 이 맥락에서 왜 (운영자) 과부하에 대해 토론해야합니까?
fredoverflow

언어에 관계없이 비슷한 개발 프로세스를 가진 프로젝트의 경우에도 동일한 문제가 발생합니다. 너무 많은 오버로드로 인해 패치 파일의 몇 줄만 있으면 변경의 영향을 이해하기 어렵습니다.
Scott Wales

@FredOverflow : Linux 커널은 실제로 GCC C에 있습니다. C에 거의 C ++ 느낌을주는 모든 종류의 확장을 사용합니다. 나는 멋진 유형 조작 중 일부를 생각하고 있습니다.
Zan Lynx

2
@Scott : C에는 기능을 오버로드 할 수있는 기능이 없기 때문에 C로 프로그래밍 된 프로젝트와 관련하여 과부하의 "악성"에 대해 논의 할 필요가 없습니다.
fredoverflow

3
Linus Torvalds는 좁은 관점을 가지고있는 것 같습니다. 그는 종종 리눅스 커널 프로그래밍에 유용하지 않은 것들이 일반적인 용도로는 적합하지 않은 것처럼 비판합니다. Subversion이 한 예입니다. 좋은 VCS이지만 Linux 커널 개발에는 실제로 분산 VCS가 필요하므로 Linus는 일반적으로 SVN을 비판했습니다.
David Thornley

2

나는 그것이 기대를 깨는 것과 관련이 있다고 생각합니다. 나는 당신이 C ++에 익숙했고, 언어에 의해 완전히 지시되지 않은 연산자 행동에 익숙해졌으며 연산자가 이상한 일을 할 때 놀라지 않을 것입니다. 해당 기능이없는 언어에 익숙하고 C ++ 코드를 보는 경우 다른 언어의 기대치를 충족 시키며 과부하 된 연산자가 펑키 한 일을하는 것을 발견하면 놀라 울 수 있습니다.

개인적으로 차이가 있다고 생각합니다. 언어의 내장 구문 동작을 변경할 수 있으면 추론하기가 더 불투명 해집니다. 메타 프로그래밍을 허용하지 않는 언어는 구문 적으로 덜 강력하지만 개념적으로 이해하기가 더 간단합니다.


과부하 된 연산자는 "이상한 무언가"를 수행해서는 안됩니다. 물론 복잡한 일을해도 괜찮습니다. 그러나 하나의 명백한 의미가있을 때만 과부하가 발생합니다.
Sjoerd

2

수학 연산자 오버로드는 C ++에서 연산자 오버로드의 실제 문제가 아니라고 생각합니다. 나는 표현의 맥락에 의존해서는 안되는 과부하 연산자 (즉 타입)는 "악"이라고 생각합니다. 예를 들어 과부하 , [ ] ( ) -> ->* new delete또는 단항 *. 절대 변경해서는 안되는 운영자의 기대치가 있습니다.


+1 []를 ++와 동일하게 만들지 마십시오.
Michael K

3
우리가 언급 한 연산자를 전혀 오버로드해서는 안된다는 말입니까? 아니면 제정신의 목적으로 만 과부하를해야한다고 말하는가? 나는없는 컨테이너 operator[], 펑터가없는 operator(), 스마트 포인터가없는 스마트 포인터 를 보는 것을 싫어하기 operator->때문입니다.
fredoverflow

수학 연산자로 연산자 오버로드의 잠재적 문제는 해당 연산자에 비해 작습니다. 수학 연산자로 영리하거나 미친 일을하는 것은 번거로울 수 있지만, 사람들이 보통 연산자로 생각하지 않고 기본 언어 요소로 생각하는 내가 언급 한 연산자는 항상 언어가 정의한 기대치를 충족 해야 합니다. []항상 배열과 같은 접근 자 ->이어야하며 항상 멤버에 액세스해야합니다. 실제로 배열이나 다른 컨테이너인지, 아니면 스마트 포인터인지 여부는 중요하지 않습니다.
Allon Guralnek

2

숨기는 것에 대한 요엘의 주장이 마음에 들지 않는다는 것을 잘 알고 있습니다. 나도 마찬가지야 내장 숫자 유형과 같은 것 또는 매트릭스와 같은 자신의 것에는 '+'를 사용하는 것이 훨씬 좋습니다. 나는 두 개의 행렬에 '.multiply ()'대신 '*'를 곱할 수있는 깔끔하고 우아하다는 것을 인정합니다. 그리고 결국 우리는 두 경우 모두 같은 종류의 추상화를 얻었습니다.

여기서 아픈 것은 코드의 가독성입니다. 실제 사례에서는 행렬 곱셈의 학문적 예가 아닙니다. 예를 들어, 언어에서 처음에 언어 핵심에없는 연산자를 정의 할 수있는 경우가 있습니다 =:=. 이 시점에서 많은 추가 질문이 발생합니다. 그 망할 운영자는 무엇입니까? 그 일의 우선 순위는 무엇입니까? 연관성은 무엇입니까? 어떤 순서로 a =:= b =:= c실제로 실행됩니까?

그것은 이미 연산자 오버로드에 대한 논쟁입니다. 아직도 확신하지 않습니까? 우선 순위 규칙을 확인하면 10 초 이상 걸리지 않습니까? 좋아, 계속하자

연산자 오버로드를 허용하는 언어 (예 : 이름이 'S'로 시작하는 언어)를 사용하기 시작하면 라이브러리 디자이너가 연산자를 재정의하는 것을 좋아한다는 것을 빨리 알게됩니다. 물론 그들은 교육을 잘 받았으며 모범 사례 (여기에는 냉소주의 없음)를 따르며 모든 API는 개별적으로 볼 때 완벽하게 이해됩니다.

이제 코드 하나에 연산자 오버로드를 많이 사용하는 몇 가지 API를 사용해야한다고 상상해보십시오. 또는 더 나은-그런 레거시 코드를 읽어야합니다. 이것은 작업자 과부하가 실제로 짜증나는 때입니다. 기본적으로 한곳에 과부하 된 연산자가 많으면 곧 프로그램 코드의 다른 비 숫자 문자와 어울리기 시작합니다. 그들은 실제로 연산자가 아닌 블록과 스코프, 모양 흐름 제어 문 또는 일부 메타 물건을 나타내는 좀 더 기본적인 언어 문법 요소가 아닌 알파벳이 아닌 문자와 섞일 것입니다. 시각적 혼란을 이해하려면 안경을 착용하고 눈을 LCD 디스플레이에 10cm 더 가까이 이동해야합니다.


1
기존 연산자를 오버로드하고 새로운 연산자를 발명하는 것은 같은 것이 아니라 나에게서 +1입니다.
fredoverflow

1

일반적으로 직관적이지 않은 방식으로 연산자 오버로드를 사용하지 않습니다. 즉, 숫자 클래스가있는 경우 과부하가 허용됩니다 (권장). 그러나 직원 클래스가있는 경우 오버로드는 어떻게됩니까? 다시 말해서, 이해하기 쉬운 직관적 인 방법으로 운영자에게 과부하를가하십시오.

받아 들여 지거나 격려되는 :

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

받아 드릴 수없는:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};

1
직원을 늘리고 있습니까? 그들이 그것이 회의실 테이블에서 그렇게한다면 그것은 바로 해고 가능한 범죄입니다.
gbjbaanb

1

여기에 이미 언급 된 것 외에도 연산자 오버로드에 대한 또 다른 주장이 있습니다. 실제로, 당신이 글을 쓴다면 +, 이것은 당신이 무언가에 무언가를 추가한다는 것을 의미합니다. 그러나 항상 그런 것은 아닙니다.

C ++ 자체는 그러한 경우의 훌륭한 예를 제공합니다. 어떻게 stream << 1읽어야합니까? 스트림이 1만큼 왼쪽으로 이동 했습니까? C ++의 <<도 스트림에 쓰라는 것을 명시 적으로 알지 않는 한 전혀 분명하지 않습니다. 그러나이 작업이 메서드로 구현 된 경우 정상적인 개발자는 쓸 o.leftShift(1)수 없으며 다음과 같습니다 o.write(1).

결론은 연산자 오버로드를 사용할 수 없게함으로써 언어는 프로그래머가 작업 이름에 대해 생각하게한다는 것입니다. 선택한 이름이 완벽하지 않더라도 이름보다 부호를 잘못 해석하는 것은 여전히 ​​어렵습니다.


1

철자법에 비해 연산자는 더 짧지 만 괄호가 필요하지 않습니다. 괄호는 입력하기가 상대적으로 불편합니다. 그리고 당신은 그것들의 균형을 잡아야합니다. 전체적으로, 모든 메소드 호출은 연산자와 비교하여 3 문자의 일반 노이즈가 필요합니다. 이것은 연산자를 사용하는 것이 매우 유혹적입니다.
왜 다른 사람이 이것을 원 cout << "Hello world"할까요?

과부하 문제는 대부분의 프로그래머가 믿을 수 없을 정도로 게으 르며 대부분의 프로그래머가 감당할 수 없다는 것입니다.

C ++ 프로그래머가 연산자 오버로드를 악용하게 만드는 것은 존재하지 않지만 메소드 호출을 수행하는 더 좋은 방법이 없다는 것입니다. 그리고 사람들은 가능하기 때문에 운영자 오버로드를 두려워하지 않고 완료 되었기 때문입니다.
예를 들어 Ruby와 Scala에서는 아무도 작업자 오버로드를 두려워하지 않습니다. 사실 연산자를 사용하는 것이 메소드보다 짧지 않다는 또 다른 이유는 Ruby가 연산자 오버로드 를 합리적인 최소값으로 제한 하고 Scala를 사용하면 자신의 연산자를 선언하여 충돌을 피할 수 있다는 것입니다.


또는 C #에서 + =를 사용하여 이벤트를 대리자와 연결하는 경우. 프로그래머 어리 석음에 대한 비난하는 언어 기능이 건설적인 방법이라고 생각하지 않습니다.
gbjbaanb

0

연산자 오버로딩이 무서워하는 이유는 *단순히 "곱하기"를 의미하지 않는 생각조차 하지 않는 많은 프로그래머들이 있기 때문입니다 . 반면 foo.multiply(bar)에 최소한 같은 프로그래머 같은 방법 은 누군가가 커스텀 곱셈 방법을 쓴다는 것을 그 프로그래머에게 즉시 지적합니다 . 그 시점에서 그들은 왜 궁금해하고 조사하러 갈 것입니다.

저는 "CompareValues"라는 메소드를 만들어서 2 개의 인수를 취하고 한 값을 다른 값으로 적용하고 부울 값을 반환하는 상위 수준의 "좋은 프로그래머"와 함께 작업했습니다. 또는 "LoadTheValues"라는 메소드를 사용하여 3 개의 다른 개체에 대해 데이터베이스로 이동하여 값을 가져 this오고 계산을 수행 하고이를 데이터베이스에 저장합니다.

이러한 유형의 프로그래머와 팀을 이루어 작업하고 있다면, 그들이 일한 것을 조사하는 것을 즉시 알고 있습니다. 그들이 연산자에 과부하를가했다면, 그들이하고 있다고 생각하는 것을 제외하고 그들이 그것을했다는 것을 전혀 알 수 없습니다.

완벽한 세계 또는 완벽한 프로그래머가있는 팀에서 운영자 과부하는 아마도 환상적인 도구 일 것입니다. 그래도 완벽한 프로그래머 팀에서 일하지는 않았으므로 그것이 무서운 이유입니다.

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