이 질문에 이미 답변이 있습니다. :
댓글
답변
Robert의 답변 에 포함 된 void *
포인터 외에도 다음과 같은 기술이 사용되었습니다 (면책 조항 : 20 년 된 메모리) :
#define WANTIMP #define TYPE int #include "collection.h" #undef TYPE #define TYPE string #include "collection.h" #undef TYPE int main() { Collection_int lstInt; Collection_string lstString; }
collection.h
내부의 정확한 전 처리기 마법을 잊은 곳이지만 다음과 같습니다.
class Collection_ ## TYPE { public: Collection_ ## TYPE () {} void Add(TYPE value); private: TYPE *list; size_t n; size_t a; } #ifdef WANTIMP void Collection_ ## TYPE ::Add(TYPE value) #endif
댓글
답변
제네릭없이 제네릭을 구현하는 전통적인 방법 (템플릿이 생성 된 이유)은 void 포인터를 사용하는 것입니다.
typedef struct Item{ void* data; } Item; typedef struct Node{ Item Item; struct Node* next; struct Node* previous; } Node;
이 예제 코드에서 바이너리 트리 또는 이중 연결 목록을 나타낼 수 있습니다. item
는 void 포인터를 캡슐화하므로 모든 데이터 유형을 저장할 수 있습니다. 물론 사용 가능한 객체로 다시 캐스팅하려면 런타임시 데이터 유형을 알아야합니다.
댓글
답변
다른 답변이 지적했듯이 일반 데이터 구조에 void*
를 사용할 수 있습니다.다른 종류의 파라 메트릭 다형성의 경우, 어떤 것이 많이 (예 : 수십 번) 반복되면 전 처리기 매크로가 사용되었습니다. 솔직히 말해서, 보통의 반복을위한 대부분의 경우 사람들은 복사하여 붙여 넣은 다음 유형을 변경했습니다. 매크로에는 문제를 일으키는 많은 함정이 있기 때문입니다.
blub paradox 의 반대 이름입니다. 사람들은 표현력이 떨어지는 언어로 프로그래밍하는 것을 상상하기가 어렵습니다. 매개 변수 다형성을 구현하는 표현적인 방법으로 언어를 사용하지 않았다면 무엇을 놓치고 있는지 알지 못합니다. 복사 및 붙여 넣기를 다소 성가 시지만 필요한 것으로 받아들입니다.
현재 선택한 언어에는 아직 인식하지 못한 비 효율성이 있습니다. 20 년 후에 사람들은 당신이 그들을 어떻게 제거했는지 궁금해 할 것입니다. 짧은 대답은 할 수 있다는 것을 몰랐기 때문에하지 않았다는 것입니다.
댓글
Answer
gcc가 genclass
와 함께 제공되었을 때 기억합니다-매개 변수 유형 집합 ( 예를 들어 Map의 키와 값)과 매개 변수화 된 유형 (예 : Map 또는 Vector)을 설명하고 매개 변수 유형이 채워진 유효한 C ++ 구현을 생성 한 특수 구문 파일.
그러면 Map<int, string>
및 Map<string, string>
(실제 구문이 아닙니다. 해당 프로그램을 두 번 실행하여 map_string_string.h 및 map_int_string.h와 같은 것을 생성 한 다음이를 코드에서 사용하십시오.
맨 페이지에서 genclass
및 GNU C ++ 라이브러리 2.0 문서 를 참조하세요.
답변
[OP에 : 나는 “당신을 개인적으로 선택하려고하는 것이 아니라 당신과 다른 사람들에게 질문을 던진다”는 질문의 논리에 대한 인식 ( s) SE 및 다른 곳에서 요청했습니다. 이 질문을 개인적으로 받아들이지 마세요!]
질문의 제목은 좋지만 컴파일 타임 코드 생성이 필요한 “… 상황을 포함하여 답변 범위를 심각하게 제한하고 있습니다. “템플릿없이 C ++에서 컴파일 타임 코드 생성을 수행하는 방법에 대한 질문에 대한 많은 좋은 답변이이 페이지에 있지만 원래 제기 한 질문에 대한 답은 다음과 같습니다.
C ++에서 템플릿 이전에 사람들은 무엇을 했나요?
물론 그들은 그것들을 사용하지 않았습니다. 예, 저는 혀를 내밀고 있지만 본문의 질문에 대한 세부 사항은 모든 사람이 템플릿을 좋아하고 템플릿 없이는 코딩을 할 수 없다고 가정하는 것 같습니다.
예를 들어, 컴파일 타임 코드 생성없이 다양한 언어로 많은 코딩 프로젝트를 완료했으며 다른 사람들도 그랬다고 생각합니다. 물론 템플릿으로 해결 된 문제는 누군가가 실제로 긁을 수있을만큼 가려 웠지만이 질문에 의해 제시된 시나리오는 대체로 존재하지 않았습니다.
자동차에서도 비슷한 질문을 생각해보십시오.
자동 변속기가 발명되기 전에 운전자가 자동으로 기어를 변속하는 방법을 사용하여 한 기어에서 다른 기어로 어떻게 변속 했습니까?
물론 문제는 어리석은 것입니다. X가 발명되기 전에 사람이 X를 어떻게했는지 묻는 것은 “정말 타당한 질문이 아닙니다. 대답은 일반적으로”우리가 그것을하지 않았고, 그것이 존재한다는 것을 몰랐기 때문에 그것을 놓치지 않았다 “입니다.예, 사후에 이점을 확인하는 것은 쉽지만 모든 사람이 서서 발을 차고 자동 전송을 기다리거나 C ++ 템플릿을 기다리고 있다고 가정하는 것은 사실이 아닙니다.
“자동 변속기가 발명되기 전에 운전자는 어떻게 기어를 변속 했습니까?”라는 질문에 대해 합리적으로 대답 할 수 있습니다. “수동”이라고 대답 할 수 있으며 이것이 바로 여기에서 얻고있는 유형입니다. 물어 보려고했던 질문의 유형일 수도 있습니다.
하지만 질문 한 것이 아닙니다.
그래서 :
Q : 템플릿이 발명되기 전에 사람들은 템플릿을 어떻게 사용 했나요?
A : 그렇지 않았습니다.
Q : 템플릿이 발명되기 전에 사람들은 템플릿을 어떻게 사용 했나요? 템플릿을 사용 하시겠습니까 ?
A : 우리는 템플릿을 사용할 필요가 없었습니다 . 우리가 사용했다고 가정하는 이유는 무엇입니까? (왜 사용한다고 가정합니까?)
Q : 템플릿이 제공하는 결과를 얻을 수있는 다른 방법은 무엇인가요?
A : 위에는 좋은 답변이 많이 있습니다.
게시하기 전에 게시물의 논리적 오류에 대해 생각해보세요.
[감사합니다. 해를 끼치 지 마십시오.]
댓글
Answer
답변
Robert Harvey가 이미 말했듯이 void 포인터는 일반 데이터 유형입니다.
표준 C 라이브러리의 예, 일반 정렬로 double 배열을 정렬하는 방법 :
p>
double *array = ...; int size = ...; qsort (array, size, sizeof (double), compare_doubles);
compare_double
는 다음과 같이 정의됩니다.
int compare_doubles (const void *a, const void *b) { const double *da = (const double *) a; const double *db = (const double *) b; return (*da > *db) - (*da < *db); }
qsort
의 서명은 stdlib.h에 정의되어 있습니다.
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) );
유형이 없습니다. 런타임이 아닌 컴파일 타임에 확인합니다. double을 예상하는 위의 비교기로 문자열 목록을 정렬하면 문자열의 이진 표현을 double로 해석하고 그에 따라 정렬하려고합니다.
Answer
이 작업을 수행하는 한 가지 방법은 다음과 같습니다.
https://github.com/rgeminas/gp–/blob/master/src/scope/darray.h
#define DARRAY_DEFINE(name, type) DARRAY_TYPEDECL(name, type) DARRAY_IMPL(name, type) // This is one single long line #define DARRAY_TYPEDECL(name, type) \ typedef struct darray_##name \ { \ type* base; \ size_t allocated_mem; \ size_t length; \ } darray_##name; // This is also a single line #define DARRAY_IMPL(name, type) \ static darray_##name* darray_init_##name() \ { \ darray_##name* arr = (darray_##name*) malloc(sizeof(darray_##name)); \ arr->base = (type*) malloc(sizeof(type)); \ arr->length = 0; \ arr->allocated_mem = 1; \ return arr; \ }
DARRAY_TYPEDECL 매크로는 name
를 전달하고 전달하는 type
의 배열을 저장합니다 (name
를 연결할 수 있도록 DARRAY_IMPL 매크로는 해당 구조체에서 작동하는 함수를 정의하는 반면, 기본 구조체 이름에 여전히 유효한 식별자가 있습니다. DARRAY_IMPL 매크로는 해당 구조체에서 작동하는 함수를 정의합니다 (이 경우 “정적으로 표시되어 정의를 한 번만 호출하고 모든 것을 분리하지 않습니다).
다음과 같이 사용됩니다.
#include "darray.h" // No types have been defined yet DARRAY_DEFINE(int_ptr, int*) // by this point, the type has been declared and its functions defined darray_int_ptr* darray = darray_int_ptr_init();
답변
템플릿은 다음과 같은 컨테이너 유형을 재사용하는 방법으로 많이 사용됩니다. 동적 배열 (벡터),지도, 트리 등 정렬과 같은 많은 알고리즘 값.
템플릿이 없으면 반드시 구현이 일반적인 방식으로 작성되고 충분한 정보가 제공됩니다. 도메인에 필요한 유형에 대해. 예를 들어, 벡터를 사용하는 경우 데이터를 blt “할 수 있어야하고 각 항목의 크기를 알아야합니다.
이 작업을 수행하는 Vector라는 컨테이너 클래스가 있다고 가정 해 보겠습니다. 무효 * 걸립니다. 이것의 단순한 사용법은 애플리케이션 레이어 코드가 많은 캐스팅을 수행하는 것입니다. 따라서 Cat 개체를 관리하는 경우 Cat *을 void *로 캐스팅하고 모든 위치로 되돌려 야합니다. 캐스트가 포함 된 애플리케이션 코드를 흩 뿌리면 명백한 문제가 있습니다.
템플릿이이 문제를 해결합니다.
이를 해결하는 또 다른 방법은 컨테이너에 저장하는 유형에 대한 사용자 정의 컨테이너 유형을 만드는 것입니다. 따라서 Cat 클래스가있는 경우 Vector에서 파생 된 CatList 클래스를 만듭니다.그런 다음 사용하는 몇 가지 메서드를 오버로드하여 void * 대신 Cat 개체를 사용하는 버전을 도입합니다. 따라서 내부적으로 Vector :: Add ()에 매개 변수를 전달하는 Cat :: Add (Cat *)를 사용하여 Vector :: Add (void *) 메서드를 오버로드합니다. 그런 다음 응용 프로그램 코드에서 Cat 개체를 전달할 때 Add의 오버로드 된 버전이 있으므로 캐스팅을 피합니다. 공정하게 말하면, Add 메서드는 “Cat * 객체가 캐스트없이 void *로 변환되기 때문에 캐스트가 필요하지 않습니다. 그러나 인덱스 오버로드 또는 Get () 메서드와 같은 항목을 검색하는 메서드는 그렇게 할 것입니다.
다른 접근 방식은 90 년대 초반부터 대규모 애플리케이션 프레임 워크에서 기억하는 유일한 예는 클래스에 대해 이러한 유형을 생성하는 사용자 지정 유틸리티를 사용하는 것입니다. MFC가이 작업을 수행했다고 생각합니다. 컨테이너에 대해 서로 다른 클래스가 있습니다. CStringArray, CRectArray, CDoulbeArray, CIntArray 등. 중복 코드를 유지하는 대신 클래스를 생성하는 외부 도구를 사용하여 매크로와 유사한 메타 프로그래밍을 수행했습니다. 사용하고 싶었습니다. 한 번도 사용하지 않았습니다. 아마도 그럴 필요가 있었지만 당시 전문가들은 “C ++의 Sane 하위 집합”및 “템플릿이 필요하지 않습니다”라고 선전했습니다.