C ++ STL이 왜 템플릿에 크게 의존하고 있습니까? (* 인터페이스 *가 아님)


211

의무적 인 이름 (표준 템플릿 라이브러리)을 제외하고는 ...

C ++은 처음에 OOP 개념을 C에 제시하려고했습니다. 즉, 클래스 및 클래스 계층 구조를 기반으로 특정 엔터티가 수행 할 수있는 작업과 수행 할 수없는 작업을 알 수 있습니다. 다중 상속의 문제와 C ++이 인터페이스 개념을 다소 어색한 방식으로 (자바와 비교하여) 지원한다는 사실로 인해 일부 기능 구성은이 방식으로 설명하기가 더 어렵습니다. 향상).

그런 다음 STL과 함께 템플릿이 작동했습니다. STL은 기존 OOP 개념을 사용하여 대신 템플릿을 사용하여 배수구를 플러시했습니다.

템플릿을 사용하여 템플릿의 형식과 관련이없는 유형을 일반화하기 위해 템플릿을 사용하는 경우 (예 : 컨테이너)가 구분되어야합니다. A가 갖는 vector<int>완벽한 의미가 있습니다.

그러나 많은 다른 경우 (반복자와 알고리즘)에서 템플릿 형식은 개념의 실제 세부 사항이 템플릿의 구현에 의해 완전히 정의되는 "개념"(입력 반복자, 순방향 반복자 등)을 따라야합니다. 템플릿과 함께 사용되는 유형의 클래스가 아닌 함수 / 클래스.

예를 들어, 함수에 다음을 알려줄 수 있습니다.

void MyFunc(ForwardIterator<...> *I);

업데이트 : 원래 질문에서 명확하지 않았으므로 ForwardIterator는 모든 ForwardIterator 유형을 허용하도록 템플릿을 작성하는 것이 좋습니다. 반대로 ForwardIterator를 개념으로 사용하고 있습니다.

Forward Iterator는 그 정의를 살펴보고 구현 또는 문서를 살펴볼 필요가있는 경우에만 기대합니다.

template <typename Type> void MyFunc(Type *I);

템플릿 사용을 선호하는 두 가지 주장 : vtable을 사용하는 대신 사용 된 각 유형에 맞게 템플릿을 컴파일하여 컴파일 된 코드를보다 효율적으로 만들 수 있습니다. 또한 템플릿을 기본 유형과 함께 사용할 수 있습니다.

그러나 STL을 템플릿 화하기 위해 클래식 OOP를 포기하는 더 깊은 이유를 찾고 있습니까? (당신이 그 거리를 읽었다 고 가정 : P)


4
stackoverflow.com/questions/31693/… 을 확인하십시오 . 허용되는 답변은 제네릭을 통해 제공되는 템플릿에 대한 훌륭한 설명입니다.
James McMahon

6
@Jonas : 말이되지 않습니다. 캐시에 대한 제약으로 인해 클럭 사이클이 발생하기 때문에 이것이 중요합니다. 하루가 끝나면 캐시가 아닌 클럭 사이클이 성능을 정의합니다. 메모리와 캐시는 사용 된 클럭 사이클에 영향을주는 한에만 중요합니다. 또한 실험을 쉽게 수행 할 수 있습니다. 예를 들어, std :: for_Each는 동등한 OOP / vtable 접근 방식으로 functor 인수로 호출되었습니다. 성능의 차이는 엄청납니다 . 템플릿 버전이 사용되는 이유입니다.
jalf 2016 년

7
중복 코드가 icache를 채우는 이유는 없습니다. 프로그램에서 vector <char> 및 vector <int>를 인스턴스화하면 vector <int>를 처리하는 동안 왜 vector <char> 코드가 icache에로드되어야합니까? 사실, vector <int>의 코드는 캐스팅, vtable 및 간접 코드를 포함 할 필요가 없기 때문에 잘립니다.
jalf

3
Alex Stepanov 왜 상속과 평등이 잘 어울리지 않는지 설명합니다 .
fredoverflow

6
@ BerndJendrissek : 음, 가까이하지만 자신은 없습니다. 예, 실제로 사용되는 경우 메모리 대역폭 및 캐시 사용 측면에서 더 많은 코드 비용이 발생합니다 . 그러나 기대하는 특별한 이유가 없다 vector<int>vector<char>동시에 사용할 수는. 그들은 물론, 수도,하지만 당신은 사용할 수 있는 동시에 코드의 두 조각을. 템플릿, C ++ 또는 STL과는 아무런 관련이 없습니다. 인스턴스화 vector<int>에는 vector<char>코드를로드하거나 실행할 필요 가 없습니다 .
jalf

답변:


607

짧은 대답은 "C ++이 발전했기 때문"입니다. 예, 70 년대 후반에 Stroustrup은 OOP 기능을 갖춘 업그레이드 된 C를 만들려고했지만 오래 전입니다. 1998 년에 언어가 표준화 될 때까지는 더 이상 OOP 언어가 아니 었습니다. 그것은 다중 패러다임 언어였습니다. 확실히 OOP 코드를 약간 지원했지만 튜링 완료 템플릿 언어가 중첩되어 컴파일 타임 메타 프로그래밍이 가능했으며 사람들은 일반 프로그래밍을 발견했습니다. 갑자기 OOP는 그다지 중요하지 않은 것처럼 보였습니다. 템플릿과 일반 프로그래밍을 통해 사용할 수있는 기술을 사용하여 더 단순하고 간결 하며 효율적인 코드를 작성할 수있는시기는 아닙니다 .

OOP는 성배가 아닙니다. 그것은 귀여운 아이디어이며, 발명 당시 70 년대에 절차 적 언어에 비해 상당히 개선되었습니다. 그러나 정직하게 모든 것이 정직하지는 않습니다. 대부분의 경우 어색하고 장황하며 재사용 가능한 코드 나 모듈성을 실제로 홍보하지는 않습니다.

이것이 바로 C ++ 커뮤니티가 오늘날 일반 프로그래밍에 훨씬 더 관심을 갖는 이유이며, 결국 모든 사람들 이 함수형 프로그래밍도 상당히 영리하다는 것을 깨닫기 시작합니다. OOP 자체는 그다지 눈에 띄지 않습니다.

가상의 "OOP 인증"STL의 종속성 그래프를 그려보십시오. 서로에 대해 얼마나 많은 수업을 알아야합니까? 많은 의존성 이있을 것 입니다. vector가져 iterator오거나 iostream끌어 들이지 않고 헤더 만 포함 할 수 있습니까? STL은 이것을 쉽게 만듭니다. 벡터는 자신이 정의한 반복자 유형에 대해 알고 있습니다. STL 알고리즘은 아무것도 모른다 . 모두 반복자를 매개 변수로 허용하더라도 반복자 헤더를 포함 할 필요조차 없습니다. 그렇다면 어느 것이 더 모듈 식입니까?

Java가 정의한대로 STL이 OOP의 규칙을 따르지 않을 수 있지만 OOP 의 목표 를 달성하지 못 합니까? 재사용 성, 낮은 커플 링, 모듈화 및 캡슐화를 달성하지 못합니까?

OOP 인증 버전보다 이러한 목표를 더 잘 달성하지 못 합니까?

STL이 언어로 채택 된 이유는 STL을 초래 한 몇 가지 일이 발생했습니다.

먼저 템플릿이 C ++에 추가되었습니다. 제네릭이 .NET에 추가 된 것과 같은 이유로 추가되었습니다. 타입 안전성을 버리지 않고 "T 타입 컨테이너"와 같은 것을 작성할 수있는 것이 좋은 생각이었습니다. 물론 그들이 구현 한 구현은 훨씬 더 복잡하고 강력했습니다.

그런 다음 사람들은 추가 한 템플릿 메커니즘이 예상보다 훨씬 강력하다는 것을 알게되었습니다. 그리고 누군가는보다 일반적인 라이브러리를 작성하기 위해 템플릿을 사용하여 실험을 시작했습니다. 함수형 프로그래밍에서 영감을 얻은 것과 C ++의 모든 새로운 기능을 사용한 것입니다.

그는 그것을 C ++ 언어위원회에 제출했는데, 그는 너무 이상하고 다르게 보였기 때문에 익숙해지기까지 꽤 오랜 시간이 걸렸지 만 궁극적 으로는 그렇지 않은 다른 전통적인 OOP보다 더 잘 작동 한다는 것을 깨달았습니다 . 그래서 그들은 몇 가지를 조정하여 표준 라이브러리에 적용했습니다.

그것은 이데올로기적인 선택이 아니라 "우리가 OOP가되고 싶은지 아닌지"의 정치적 선택이 아니라 매우 실용적인 선택이었습니다. 그들은 도서관을 평가하고 잘 작동하는 것을 보았습니다.

어쨌든 STL을 선호한다고 언급 한 두 가지 이유는 모두 필수적입니다.

C ++ 표준 라이브러리 효율적이어야합니다. 예를 들어, 동등한 수동 롤링 C 코드보다 효율성이 떨어지면 사람들은이 코드를 사용하지 않을 것입니다. 이는 생산성을 낮추고 버그 가능성을 높이며 전반적으로 나쁜 생각입니다.

STL 프리미티브 유형과 함께 작동해야합니다. 프리미티브 유형은 C에있는 모든 것이기 때문에 두 언어의 주요 부분이기 때문입니다. STL이 기본 배열에서 작동하지 않으면 쓸모없습니다 .

귀하의 질문에는 OOP가 "최고"라고 강력하게 가정합니다. 왜 그런지 궁금합니다. 당신은 왜 그들이 "고전 OOP를 폐지"했는지 묻습니다. 나는 그들이 왜 붙어 있어야하는지 궁금합니다. 어떤 장점이 있었습니까?


22
좋은 글이지만, 한 가지 세부 사항을 강조하고 싶습니다. STL은 C ++의 "제품"이 아닙니다. 사실, 개념으로서의 STL은 C ++ 이전에 존재했으며 C ++은 일반 프로그래밍을위한 (거의) 충분한 힘을 가진 효율적인 언어가되었으므로 STL은 C ++로 작성되었습니다.
Igor Krivokon 2016 년

17
의견이 계속 나오기 때문에 STL 이름이 모호하다는 것을 알고 있습니다. 그러나 "STL에서 모델링 된 C ++ 표준 라이브러리의 일부"라는 더 나은 이름은 생각할 수 없습니다. 표준 라이브러리의 해당 부분에 대한 사실상의 이름 "STL"입니다. 비록 정확하지는 않습니다. :) 사람들이 STL을 전체 표준 라이브러리 (IOStream 및 C stdlib 헤더 포함) 의 이름으로 사용하지 않는 한 행복합니다. :)
jalf

5
@einpoklum 그리고 추상 기본 클래스에서 정확히 무엇을 얻을 것입니까? 가지고 std::set예를 들어. 추상 기본 클래스에서 상속되지 않습니다. 그게 어떻게 당신의 사용을 제한 std::set합니까? std::set추상 기본 클래스에서 상속받지 않기 때문에 할 수없는 것이 있습니까?
fredoverflow 2014 년

22
@einpoklum Alan Kay가 OOP라는 용어를 발명 할 때 OOP 언어로 디자인 한 스몰 토크 언어를 살펴보십시오. 인터페이스가 없었습니다. OOP는 인터페이스 나 추상 기본 클래스에 관한 것이 아닙니다. 당신은 "생각했던 용어 OOP의 발명자가 C ++보다 더 OOP 무엇처럼 아무것도 자바, 말 건가요 또한 용어 OOP의 발명자가 생각했던 것처럼 아무것도 없다"를? 당신이 말하는 것은 "C ++는 내 취향에 맞게 Java와는 다르다"는 것입니다. 그것은 공평하지만 OOP와는 아무런 관련없습니다 .
jalf

8
@MasonWheeler 만약이 답변이 엄청나게 터무니없는 말이 아니라면, 전 세계 수백 명의 개발자가 말 그대로 세 명만 투표하고 +1 투표하지 않을 것입니다.
panda-34

88

내가 생각하고 불평하는 것에 대한 가장 직접적인 대답은 이것입니다. C ++이 OOP 언어라는 가정은 잘못된 가정입니다.

C ++는 다중 패러다임 언어입니다. OOP 원칙을 사용하여 프로그래밍 할 수 있고, 절차 적으로 프로그래밍 할 수 있으며, 일반적으로 (템플릿) 프로그래밍 할 수 있으며 C ++ 11 (이전의 C ++ 0x)을 사용하여 일부 기능을 프로그래밍 할 수도 있습니다.

C ++의 설계자들은이 점을 이점으로보고 있으므로, 일반 프로그래밍이 문제를 더 잘 해결하고보다 일반적으로 더 한 단계 뒤 떨어질 때 C ++을 순전히 OOP 언어처럼 작동하도록 제한 합니다.


4
"C ++ 0x를 사용하면 기능적으로 프로그래밍 할 수있는 기능도 있습니다."기능이 없어도 기능적으로 프로그래밍 할 수 있습니다.
Jonas Kölker

3
@Tyler 실제로 C ++를 순수 OOP로 제한하면 Objective-C가 남게됩니다.
Justicle

@TylerMcHenry : 그냥 물었다 데 , 나는 당신과 같은 대답을 발언 한 찾아라! 한 점만 표준 라이브러리를 사용하여 객체 지향 코드를 작성할 수 없다는 사실을 추가하고 싶습니다.
einpoklum

74

내 이해는 Stroustrup은 원래 "OOP 스타일"컨테이너 디자인을 선호했으며 실제로 다른 방법으로는 보지 못했다는 것입니다. Alexander Stepanov는 STL을 책임지는 사람이며 그의 목표에는 "객체 지향"을 포함하지 않았습니다 .

이것이 기본 요점입니다. 알고리즘은 대수 구조에 정의되어 있습니다. 규칙적인 공리에 복잡성 요구 사항을 추가하여 구조 개념을 확장해야한다는 것을 깨달으려면 몇 년이 더 걸렸습니다. ... 반복자 이론은 반지 또는 Banach 공간 이론이 수학의 중심이기 때문에 컴퓨터 과학의 중심이라고 생각합니다. 알고리즘을 볼 때마다 알고리즘이 정의 된 구조를 찾으려고 노력합니다. 그래서 내가하고 싶었던 것은 알고리즘을 일반적으로 설명하는 것이 었습니다. 그게 내가 좋아하는 일입니다. 한 달 동안 잘 알려진 알고리즘을 사용하여 일반적인 표현을 찾으려고 노력할 수 있습니다. ...

적어도 저에게 STL은 프로그래밍이 가능한 유일한 방법을 나타냅니다. 실제로 C ++ 프로그래밍과는 상당히 다르며 여전히 대부분의 교과서에 제시되어 있습니다. 그러나 C ++로 프로그래밍하지 않고 소프트웨어를 처리하는 올바른 방법을 찾으려고했습니다. ...

나는 많은 잘못된 시작을했다. 예를 들어, 그 메커니즘이 근본적으로 결함이 있고 사용되어서는 안되는 이유를 이해하기 전에 상속과 가상에 대한 용도를 찾기 위해 수년을 보냈습니다. 나는 아무도 모든 중간 단계를 볼 수 없다는 것을 매우 기쁘게 생각합니다. 대부분은 매우 어리 석었습니다.

(그는 인터뷰와 관련하여 객체 지향 디자인 (일명 객체 지향 디자인이 근본적으로 결함이있어서 사용해서는 안 됨) 인 상속과 가상을 설명합니다.)

Stepanov가 자신의 라이브러리를 Stroustrup에 제출하면 Stroustrup과 다른 사람들은 ISO C ++ 표준 (동일한 인터뷰)으로 가져 오기 위해 철저한 노력을 기울였습니다.

Bjarne Stroustrup의 지원은 매우 중요했습니다. Bjarne은 표준에서 STL을 정말로 원했고 Bjarne이 무언가를 원한다면 그것을 얻습니다. ... 그는 내가 다른 사람에게는 절대로하지 않을 STL을 변경하도록 강요했습니다 ... 그는 내가 아는 가장 독창적 인 사람입니다. 그는 일을 끝냅니다. 그는 STL이 무엇인지 이해하는 데 시간이 걸렸지 만, 그렇게했을 때 그는 그것을 통과 할 준비가되었습니다. 그는 또한 10여 년 동안 플락과 과대 광고가 끝나지 않았으며 유연성, 효율성, 과부하 및 형식 안전성의 조합을 추구하는 하나 이상의 프로그래밍 방법이 유효하다는 견해를 견지함으로써 STL에 기여했습니다. STL을 가능하게 한 템플릿. 나는 Bjarne이 우리 세대의 저명한 언어 디자이너라고 분명히 말하고 싶습니다.


2
재미있는 인터뷰. 꽤 오래 전에 읽었지만 확실히 다시 할 가치가 있습니다. :)
jalf 2016 년

3
내가 읽은 프로그래밍에 대한 가장 흥미로운 인터뷰 중 하나. 더 자세한 내용은
갈증을 느끼지만

Java와 같은 언어에 대한 많은 불만 ( "일부 유형의 두 인수를 사용하고 동일한 유형의 리턴 값을 갖는 Java에서 일반 max ()을 작성할 수 없음")은 초기 버전에만 관련이있었습니다. 제네릭이 추가되기 전에 언어의 비록 처음부터 제네릭이 결국 추가 될 것이라는 것이 알려져 있었지만 (한 번 실행 가능한 구문 / 의미론이 밝혀지면) 그의 비판은 거의 근거가 없다. 예, 정적으로 형식이 지정된 언어로 형식 안전을 유지하려면 일부 형식의 제네릭이 필요하지만 OO를 무가치하게 만들지는 않습니다.
일부 남자

1
@SomeGuy Java 자체에 대한 불만은 아닙니다. 그는 " SmallTalk의"표준 "OO 프로그래밍 또는 Java "에 대해 이야기하고있다 . 인터뷰는 90 년대 후반의 내용입니다 (그는 2000 년 AT & T에서 근무하기 위해 떠난 SGI에서 일하는 것을 언급합니다). 제네릭은 2004 년 1.5 버전의 Java에만 추가되었으며 "표준"OO 모델과 다른 것입니다.
melpomene 2016 년

24

답은 STL의 저자 인 Stepanov 와의 인터뷰 에서 찾을 수 있습니다 .

예. STL은 객체 지향이 아닙니다. 저는 객체 지향성이 인공 지능만큼이나 허위라고 생각합니다. 나는이 OO 사람들로부터 오는 흥미로운 코드 조각을 아직 보지 못했습니다.


좋은 보석; 몇 년인지 아십니까?
코스

2
@Kos의에 따라 web.archive.org/web/20000607205939/http://www.stlport.org/... 링크 된 페이지의 첫 번째 버전은 하단에있는 페이지 자체는 저작권 말한다 6월 7일 2001 년부터입니다 2001 2008.
alfC

@ Kos Stepanov는 첫 번째 답변에서 SGI에서의 작업에 대해 언급했습니다. 그는 2000 년 5 월에 SGI를 떠났으므로 인터뷰는 그보다 오래되었다.
melpomene 2016 년

18

데이터 구조 및 알고리즘 라이브러리에 대한 순수한 OOP 설계가 더 좋은 이유는 무엇입니까?! OOP가 모든 것에 대한 해결책은 아닙니다.

IMHO, STL은 내가 본 것 중 가장 우아한 라이브러리입니다 :)

당신의 질문에

런타임 다형성이 필요하지 않습니다. STL이 실제로 정적 다형성을 사용하여 라이브러리를 구현하는 것이 유리합니다. 즉, 효율성을 의미합니다. 일반적인 정렬 또는 거리 또는 모든 컨테이너에 적용되는 알고리즘을 작성하십시오! Sort in Java는 n 레벨을 통해 동적으로 실행되는 함수를 호출합니다!

Pure OOP 언어의 불쾌한 가정을 숨기려면 Boxing 및 Unboxing과 같은 어리석은 것이 필요합니다.

STL에서 볼 수있는 유일한 문제는 일반적으로 템플릿은 끔찍한 오류 메시지입니다. C ++ 0X에서 Concepts를 사용하여 해결할 수 있습니다.

STL과 Java의 컬렉션을 비교하는 것은 Taj Mahal을 내 집과 비교하는 것과 같습니다. :)


12
타지 마할 (Taj Mahal)은 작고 우아하며 당신의 집은 산의 크기이며 완전한 엉망입니까? ;)
jalf 2016 년

개념은 더 이상 c ++ 0x의 일부가 아닙니다. 일부 오류 메시지는 static_assert아마도 사용하여 선점 할 수 있습니다 .
KitsuneYMG

GCC 4.6은 템플릿 오류 메시지를 개선했으며 4.7 이상이 더 좋습니다.
David Stone

개념은 기본적으로 OP가 요구 한 "인터페이스"입니다. 유일한 차이점은 개념의 "상속"이 명시 적이 아니라 (클래스에 모든 올바른 멤버 함수가있는 경우 자동으로 개념의 하위 유형 임) 내재적이라는 것입니다 (Java 클래스는 명시 적으로 인터페이스 구현을 선언해야 함). . 그러나 암시 적 및 명시 적 서브 타이핑은 모두 유효한 OO이며 일부 OO 언어에는 암시 적 상속이있어 개념과 동일하게 작동합니다. 여기서 말하는 것은 기본적으로 "OO 짜증 : 템플릿 사용. 템플릿에는 문제가 있으므로 개념 (OO)을 사용하십시오."
일부 남자

11

템플릿 형식은 개념의 실제 세부 사항이 형식의 클래스가 아닌 템플릿 함수 / 클래스의 구현에 의해 완전히 정의되는 "개념"(Input Iterator, Forward Iterator 등)을 따라야합니다. OOP를 다소 사용하지 않는 템플릿과 함께 사용됩니다.

템플릿의 개념 사용을 오해하고 있다고 생각합니다. 예를 들어 Forward Iterator는 매우 잘 정의 된 개념입니다. 클래스가 Forward Iterator가되기 위해 유효한 표현식과 계산 복잡도를 포함한 의미를 찾으려면 표준 또는 http://www.sgi.com/tech/stl/ForwardIterator.html참조하십시오. (입력, 출력 및 사소한 반복기에 대한 링크를 따라 가야합니다).

이 문서는 완벽하게 훌륭한 인터페이스이며 "개념의 실제 세부 사항"이 바로 정의되어 있습니다. 그것들은 Forward Iterators의 구현에 의해 정의되지 않으며 Forward Iterators를 사용하는 알고리즘에 의해 정의되지도 않습니다.

STL과 Java 간 인터페이스 처리 방식의 차이점은 세 가지입니다.

1) STL은 객체를 사용하여 유효한 표현식을 정의하는 반면 Java는 객체에서 호출 해야하는 메소드를 정의합니다. 물론 유효한 식은 메서드 (멤버 함수) 호출 일 수 있지만 반드시 그럴 필요는 없습니다.

2) Java 인터페이스는 런타임 객체이지만 STL 개념은 RTTI에서도 런타임에 표시되지 않습니다.

3) STL 개념에 필요한 유효한 표현식을 유효하게 만들지 않으면 유형으로 일부 템플리트를 인스턴스화 할 때 지정되지 않은 컴파일 오류가 발생합니다. Java 인터페이스의 필수 메소드를 구현하지 못하면 특정 컴파일 오류가 발생합니다.

이 세 번째 부분은 일종의 (컴파일 타임) "덕 타이핑"을 좋아하는 경우입니다. 인터페이스는 암시적일 수 있습니다. Java에서 인터페이스는 다소 명시 적입니다. 클래스 "is"Iterable 을 구현 한다고 말하는 경우에만 Iterable입니다. 컴파일러는 메소드의 시그니처가 모두 존재하고 올바른지 확인할 수 있지만 시맨틱은 여전히 ​​암시 적입니다 (즉, 문서화되어 있는지 아닌지, 더 많은 코드 (단위 테스트)만이 구현이 올바른지 여부를 알려줄 수 있음).

C ++에서는 파이썬과 마찬가지로 의미론과 구문이 모두 암시 적이지만 C ++ (및 강력한 타이핑 전처리기를 사용하는 경우 Python)에서는 컴파일러의 도움을받습니다. 프로그래머가 구현 클래스에 의해 Java와 같은 명시 적 인터페이스 선언을 요구하는 경우 표준 접근 방식은 유형 특성을 사용하는 것입니다 (다중 상속으로 인해 너무 자세한 정보를 방지 할 수 있음). Java와 비교하여 부족한 것은 내 유형으로 인스턴스화 할 수있는 단일 템플릿이며 필요한 모든 표현식이 내 유형에 유효한 경우에만 컴파일됩니다. 이것은 내가 사용하기 전에 필요한 모든 비트를 구현했는지 여부를 알려줍니다. 그것은 편리하지만 OOP의 핵심은 아닙니다 (그리고 여전히 의미를 테스트하지는 않습니다.

STL은 귀하의 취향에 따라 충분히 OO 일 수도 있고 아닐 수도 있지만, 인터페이스와 구현을 확실히 분리합니다. 인터페이스를 통해 리플렉션을 수행 할 수있는 Java 기능이 부족하며 인터페이스 요구 사항 위반을 다르게보고합니다.

당신은 함수를 말할 수 있습니다 ... 구현을 보거나 문서를 볼 필요가있는 정의를보고 만 순방향 반복자를 기대합니다 ...

개인적으로 암시 적 유형은 적절하게 사용될 때 강점이라고 생각합니다. 알고리즘은 템플릿 매개 변수로 수행하는 작업을 말하고 구현자는 이러한 작업이 작동하는지 확인합니다. "인터페이스"가 수행해야하는 작업의 공통 분모입니다. 또한 STL을 사용 std::copy하면 헤더 파일에서 순방향 선언을 찾는 데 사용하지 않을 것 입니다. 프로그래머 함수 서명 만이 아니라 문서를 기반으로 함수가 수행하는 작업을 수행 해야 합니다. C ++, Python 또는 Java에서 마찬가지입니다. 어떤 언어로 입력하면 얻을 수있는 작업에 제한이 있으며 입력을 사용하여 수행하지 않는 작업 (의미 검사)은 오류가됩니다.

즉, STL 알고리즘은 일반적으로 필요한 개념을 명확하게하는 방식으로 템플릿 매개 변수의 이름을 지정합니다. 그러나 이것은 문서의 첫 번째 줄에 유용한 추가 정보를 제공하고 앞으로 선언을 더 유익하게 만들지 않습니다. 매개 변수 유형으로 캡슐화 할 수있는 것보다 알아야 할 것이 많으므로 문서를 읽어야합니다. (예를 들어, 입력 범위와 출력 반복자를 취하는 알고리즘에서, 출력 반복기가 입력 범위의 크기와 그 값에 따라 특정 수의 출력에 대해 충분한 "공간"을 필요로 할 가능성이 있습니다. 입력을 강력하게 입력하십시오. )

명시 적으로 선언 된 인터페이스에 대한 Bjarne은 다음과 같습니다. http://www.artima.com/cppsource/cpp0xP.html

제네릭에서 인수는 제네릭의 정의에 지정된 인터페이스 (인터페이스에 해당하는 C ++)에서 파생 된 클래스 여야합니다. 즉, 모든 일반 인수 유형은 계층 구조에 맞아야합니다. 따라서 설계에 불필요한 제약이 가해지면 개발자 측에 대한 합리적인 예측이 필요합니다. 예를 들어 제네릭을 작성하고 클래스를 정의하면 사용자가 지정한 인터페이스에 대해 알고 클래스에서 클래스를 파생시키지 않으면 클래스를 제네릭의 인수로 사용할 수 없습니다. 딱딱하다.

다른 방법으로 살펴보면, 오리 타이핑을 사용하면 인터페이스가 존재하는지 몰라도 인터페이스를 구현할 수 있습니다. 또는 누군가 클래스에서 구현하도록 인터페이스를 의도적으로 작성하여 문서를 참조하여 아직하지 않은 것을 요청하지 않는지 확인하십시오. 융통성이 있습니다.


명시 적으로 선언 된 인터페이스에서 두 가지 단어 : 유형 클래스. (이미 Stepanov가 "개념"으로 의미하는 바가 있습니다.)
pyon

"STL 개념에 필요한 유효한 식을 유효하게 만들지 못하면 형식이있는 템플릿을 인스턴스화 할 때 지정되지 않은 컴파일 오류가 발생합니다." -맞습니다. std개념과 일치하지 않는 라이브러리 로 무언가를 전달 하는 것은 일반적으로 "잘못된 형식이며 진단이 필요하지 않습니다".
Yakk-Adam Nevraumont

사실, 나는 "유효한"이라는 용어로 빠르고 느슨하게 연주했다. 컴파일러가 필요한 표현식 중 하나를 컴파일 할 수 없다면 무언가를보고 할 것입니다.
Steve Jessop

8

"OOP는 메시징, 로컬 보관 및 상태 프로세스의 보호 및 숨기기, 모든 사물의 극단적 인 바인딩 만 의미합니다. 스몰 토크와 LISP에서 수행 될 수 있습니다. 이것이 가능한 다른 시스템도있을 수 있지만 나는 그것들을 모른다. " -스몰 토크 제작자 Alan Kay.

C ++, Java 및 대부분의 다른 언어는 모두 고전적인 OOP와는 거리가 멀다. 그러나 이데올로기를 주장하는 것은 굉장히 생산적이지 않다. C ++은 어떤 의미에서나 순수하지 않으므로 당시에는 실용적으로 보이는 기능을 구현합니다.


7

STL은 가장 일반적으로 사용되는 알고리즘을 다루는 대규모 라이브러리를 제공하려는 의도로 시작되었습니다. performance)를 . 템플릿은 그러한 구현과 목표를 실현하기위한 핵심 요소가되었습니다.

다른 참조를 제공하기 위해 :

Al Stevens 인터뷰 DDJ 1995 년 3 월 Alex Stepanov :

스테파노 프는 자신의 작업 경험과 선택이 큰 알고리즘 라이브러리를 향한 선택에 대해 설명했으며 결국 STL로 발전했습니다.

일반 프로그래밍에 대한 귀하의 장기적인 관심사에 대해 알려주십시오

..... 그런 다음 Bell Laboratories에서 C ++ 그룹의 C ++ 라이브러리 관련 작업을 수행했습니다. 그들은 C ++로 할 수 있는지 물었습니다. 물론 저는 C ++을 몰랐으며 물론 가능하다고 말했습니다. 그러나 C ++에는 1987 년 에이 스타일의 프로그래밍을 활성화하는 데 필요한 템플릿이 없었기 때문에 C ++에서 할 수 없었습니다. 상속은 일반성을 얻는 유일한 메커니즘이며 충분하지 않았습니다.

지금도 C ++ 상속은 일반 프로그래밍에 많이 사용되지 않습니다. 이유를 논의 해 봅시다. 많은 사람들이 상속을 사용하여 데이터 구조와 컨테이너 클래스를 구현하려고 시도했습니다. 아시다시피 성공적인 시도가 거의 없었습니다. C ++ 상속 및 이와 관련된 프로그래밍 스타일은 크게 제한됩니다. 그것을 사용하는 것만 큼 사소한 것을 포함하는 디자인을 구현하는 것은 불가능합니다. 계층 구조의 루트에서 기본 클래스 X로 시작하고이 클래스에서 X 유형의 인수를 사용하는 가상 동등 연산자를 정의한 경우 클래스 X에서 클래스 Y를 파생시킵니다. 평등의 인터페이스는 무엇입니까? 예를 들어 동물을 예로 들어 (OO 사람들은 동물을 좋아함) 포유류를 정의하고 포유류에서 기린을 추출합니다. 그런 다음 멤버 함수 메이트를 정의하십시오. 동물이 동물과 짝을 이루어 동물을 반환합니다. 그런 다음 동물에서 기린을 추출하고 기린이 동물과 짝을 이루어 동물을 반환하는 기능 메이트가 있습니다. 확실히 당신이 원하는 것이 아닙니다. 짝짓기는 C ++ 프로그래머에게는 그다지 중요하지 않지만, 평등은 중요합니다. 나는 어떤 종류의 평등이 사용되지 않는 단일 알고리즘을 모른다.


5

기본 문제

void MyFunc(ForwardIterator *I);

iterator가 반환하는 유형을 어떻게 안전하게 얻을 수 있습니까? 템플릿을 사용하면 컴파일시이 작업이 수행됩니다.


1
글쎄, 나도 : 1. 일반 코드를 작성하고 있기 때문에 그것을 얻으려고하지 마십시오. 또는 2. 요즘 C ++에서 제공하는 모든 반사 메커니즘을 사용하여 가져옵니다.
einpoklum

2

잠시 동안 표준 라이브러리를 기본적으로 컬렉션 및 알고리즘의 데이터베이스로 생각하십시오.

데이터베이스의 역사를 연구했다면, 처음에 데이터베이스가 대부분 "계층 적"이라는 것을 의심 할 여지가 없습니다. 계층 적 데이터베이스는 클래식 OOP, 특히 스몰 토크에서 사용되는 단일 상속 다양성과 매우 밀접한 관련이 있습니다.

시간이 지남에 따라 계층 데이터베이스는 거의 모든 것을 모델링하는 데 사용될 수 있지만 경우에 따라 단일 상속 모델은 상당히 제한적이었습니다. 나무로 된 문이 있다면 문이나 일부 원료 (강철, 나무 등)로 볼 수있어 편리했습니다.

그래서 그들은 네트워크 모델 데이터베이스를 발명했습니다. 네트워크 모델 데이터베이스는 다중 상속과 매우 유사합니다. C ++은 다중 상속을 완전히 지원하는 반면 Java는 제한된 형식을 지원합니다 (한 클래스에서만 상속 할 수 있지만 원하는만큼 인터페이스를 구현할 수도 있음).

계층 적 모델 데이터베이스와 네트워크 모델 데이터베이스는 대부분 범용 용도에서 퇴색되었습니다 (몇몇은 상당히 구체적인 틈새 시장에 남아 있지만). 대부분의 경우 관계형 데이터베이스로 대체되었습니다.

관계형 데이터베이스가 인수 한 많은 이유는 다양성입니다. 관계형 모델은 기능적으로 네트워크 모델의 수퍼 세트 (즉, 계층 적 모델의 수퍼 세트)입니다.

C ++은 거의 같은 경로를 따랐습니다. 단일 상속과 계층 적 모델 사이, 그리고 다중 상속과 네트워크 모델 사이의 대응은 매우 분명합니다. C ++ 템플릿과 계층 적 모델 간의 대응 관계는 명확하지 않지만 어쨌든 매우 적합합니다.

나는 그것의 공식적인 증거를 보지 못했지만 템플릿의 기능은 다중 상속 (단일 무차별의 수퍼 세트)이 제공하는 것의 수퍼 세트라고 생각합니다. 가장 까다로운 부분은 템플릿이 대부분 정적으로 바인딩되어 있다는 것입니다. 즉, 모든 바인딩은 런타임이 아닌 컴파일 타임에 발생합니다. 따라서 상속이 상속 능력의 상위 집합을 제공한다는 공식적인 증거는 다소 어렵고 복잡 할 수 있습니다 (또는 불가능할 수도 있음).

어쨌든 C ++이 컨테이너에 상속을 사용하지 않는 가장 큰 이유라고 생각합니다. 상속은 템플릿이 제공하는 기능의 일부만 제공하기 때문에 그렇게 할 이유가 없습니다. 경우에 따라 템플릿이 기본적으로 필요하기 때문에 거의 모든 곳에서 사용할 수 있습니다.


0

ForwardIterator *와 어떻게 비교합니까? 즉, 가지고있는 항목이 원하는 항목인지, 또는 지나간 항목인지 어떻게 확인합니까?

대부분의 경우 다음과 같은 것을 사용합니다.

void MyFunc(ForwardIterator<MyType>& i)

즉, 나는 내가 MyType을 가리키고 있음을 알고 있으며, 그것들을 비교하는 방법을 알고 있습니다. 템플릿처럼 보이지만 실제로는 "템플릿"키워드가 아닙니다.


타입의 <,> 및 = 연산자를 사용하면 어떤 연산자인지 알 수 없습니다 (이것이 의도 한 것이 아닐 수도 있음)
lhahne

상황에 따라 의미가 없거나 제대로 작동 할 수 있습니다. 아마도 사용자가하는 MyType에 대해 더 알지 못하고 말하기 어렵습니다.
Tanktalus 2016 년

0

이 질문에는 많은 훌륭한 답변이 있습니다. 템플릿이 개방형 디자인을 지원한다는 점도 언급해야합니다. 객체 지향 프로그래밍 언어의 현재 상태에서는 그러한 문제를 처리 할 때 방문자 패턴을 사용해야하며, 진정한 OOP는 다중 동적 바인딩을 지원해야합니다. C ++, P. Pirkelbauer 등을위한 Open Multi-Methods를 참조하십시오.매우 흥미로운 독서를 위해.

템플릿의 또 다른 흥미로운 점은 런타임 다형성에도 사용할 수 있다는 것입니다. 예를 들어

template<class Value,class T>
Value euler_fwd(size_t N,double t_0,double t_end,Value y_0,const T& func)
    {
    auto dt=(t_end-t_0)/N;
    for(size_t k=0;k<N;++k)
        {y_0+=func(t_0 + k*dt,y_0)*dt;}
    return y_0;
    }

이 함수는 std :: vector가 아닌Value 일종의 벡터 인 경우에도 작동합니다.std::dynamic_array ). 혼란을 피하기 위해

경우 func작은,이 기능은 인라인 많은 것을 얻을 것입니다. 사용법 예

auto result=euler_fwd(10000,0.0,1.0,1.0,[](double x,double y)
    {return y;});

이 경우 정확한 답 (2.718 ...)을 알아야하지만 기본 솔루션없이 간단한 ODE를 구성하는 것은 쉽습니다 (힌트 : y에서 다항식 사용).

이제는에 큰 표현이 func있고 여러 곳에서 ODE 솔버를 사용하므로 실행 파일이 어디서나 템플릿 인스턴스화로 오염됩니다. 무엇을해야합니까? 가장 먼저 알아야 할 것은 일반 함수 포인터가 작동한다는 것입니다. 그런 다음 커리를 추가하여 인터페이스와 명시 적 인스턴스화를 작성하십시오.

class OdeFunction
    {
    public:
        virtual double operator()(double t,double y) const=0;
    };

template
double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction& func);

그러나 위의 인스턴스화는에서만 작동 double하므로 인터페이스를 템플릿으로 작성하지 마십시오.

template<class Value=double>
class OdeFunction
    {
    public:
        virtual Value operator()(double t,const Value& y) const=0;
    };

일반적인 가치 유형을 전문으로합니다.

template double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction<double>& func);

template vec4_t<double> euler_fwd(size_t N,double t_0,double t_end,vec4_t<double> y_0,const OdeFunction< vec4_t<double> >& func); // (Native AVX vector with four components)

template vec8_t<float> euler_fwd(size_t N,double t_0,double t_end,vec8_t<float> y_0,const OdeFunction< vec8_t<float> >& func); // (Native AVX vector with 8 components)

template Vector<double> euler_fwd(size_t N,double t_0,double t_end,Vector<double> y_0,const OdeFunction< Vector<double> >& func); // (A N-dimensional real vector, *not* `std::vector`, see above)

함수가 먼저 인터페이스를 중심으로 설계된 경우 해당 ABC에서 상속해야합니다. 이제이 옵션과 함수 포인터, 람다 또는 기타 함수 객체가 있습니다. 여기서 핵심은을 가져야 operator()()하며 리턴 유형에 산술 연산자를 사용할 수 있어야한다는 것입니다. 따라서 C ++에 연산자 오버로드가없는 경우 템플릿 기계가 중단됩니다.


-1

인터페이스와 인터페이스를 분리하고 구현을 교체 할 수 있다는 개념은 객체 지향 프로그래밍에 본질적인 것은 아닙니다. Microsoft COM과 같은 구성 요소 기반 개발에서 부화 된 아이디어라고 생각합니다. ( 컴포넌트 중심 개발이란 무엇입니까?에 대한 나의 답변 을 참조하십시오 .) C ++을 성장시키고 배우면서 사람들은 상속과 다형성을 과장했습니다. 90 년대 사람들은 "구현"이 아니라 "인터페이스에 대한 프로그램"과 "클래스 상속"에 대한 "객체 구성"을 좋아하기 시작했다. (둘 다 GoF에서 인용했습니다).

그런 다음 Java는 내장 가비지 수집기 및 interface키워드 와 함께 나타 났으며 갑자기 인터페이스와 구현을 실제로 분리하는 것이 실용화되었습니다. 그것을 알기 전에 아이디어는 OO의 일부가되었습니다. C ++, 템플릿 및 STL이이 모든 것보다 앞서 있습니다.


인터페이스는 단지 OO가 아니라는 점에 동의했습니다. 그러나 타입 시스템의 다형성 능력은 60 년대의 Simula에있었습니다. 모듈 인터페이스는 Modula-2와 Ada에 존재했지만 유형 시스템에서 다르게 작동한다고 생각합니다.
andygavin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.