C ++의 내부 typedef-좋은 스타일 또는 나쁜 스타일?


179

내가 최근에 자주 발견 한 것은 해당 클래스 내의 특정 클래스와 관련된 typedef를 선언하는 것입니다.

class Lorem
{
    typedef boost::shared_ptr<Lorem> ptr;
    typedef std::vector<Lorem::ptr>  vector;

//
// ...
//
};

이러한 유형은 코드의 다른 곳에서 사용됩니다.

Lorem::vector lorems;
Lorem::ptr    lorem( new Lorem() );

lorems.push_back( lorem );

내가 좋아하는 이유 :

  • 클래스 템플릿에서 발생하는 노이즈를 줄이고 등 std::vector<Lorem>이됩니다 Lorem::vector.
  • 의도의 진술로 사용됩니다. 위의 예에서 Lorem 클래스는 참조 횟수를 통해 계산 boost::shared_ptr되고 벡터에 저장됩니다.
  • 이를 통해 구현이 변경 될 수 있습니다. 즉, Lorem boost::intrusive_ptr이 이후 단계에서 (참조를 통해 ) 참조 횟수로 변경되도록 변경해야하는 경우 이는 코드에 최소한의 영향을 미칩니다.
  • 나는 그것이 더 예쁘고 읽기 쉽다고 생각합니다.

내가 싫어하는 이유 :

  • 의존성에 문제가있는 경우가 있습니다- Lorem::vector다른 클래스 내에 포함하고 싶지만 Loem 선언을 전달 해야하는 경우 (또는 헤더 파일에 의존성을 도입하는 것이 아니라) 원하는 경우 약간 일치하지 않는 명시 적 유형 (예 : boost::shared_ptr<Lorem>대신 Lorem::ptr).
  • 매우 일반적이지 않으므로 이해하기 어려울 수 있습니까?

나는 코딩 스타일에 객관적이 되려고 노력하기 때문에 그것에 대한 다른 의견을 얻는 것이 좋을 것입니다.

답변:


153

나는 그것이 훌륭한 스타일이라고 생각하고 직접 사용합니다. 가능한 한 이름의 범위를 제한하는 것이 항상 최선이며 클래스 사용은 C ++에서이를 수행하는 가장 좋은 방법입니다. 예를 들어 C ++ 표준 라이브러리는 클래스 내에서 typedef를 많이 사용합니다.


좋은 지적입니다. '더 예쁘다'는 것은 제한된 범위가 좋은 것임을 미묘하게 지적하는 잠재 의식이었던 것 같습니다. 그래도 STL이 클래스 템플릿에서 STL을 주로 사용한다는 사실이 미묘하게 다른 사용법이 될까요? '콘크리트'클래스에서 정당화하기가 더 어렵습니까?
Will Baker

1
표준 라이브러리는 클래스가 아닌 템플릿으로 구성되어 있지만 그 정당성은 양쪽 모두 동일하다고 생각합니다.

14

의도의 진술로 사용됩니다. 위의 예에서 Lorem 클래스는 boost :: shared_ptr을 통해 참조 카운트되고 벡터에 저장됩니다.

이것이 정확히하지 않는 것입니다.

코드에 'Foo :: Ptr'이 표시되면 그것이 shared_ptr인지 Foo * (STL에 :: pointer typedef가 T *인지 기억하십시오)인지 또는 다른 것이 있는지 전혀 모릅니다. Esp. 공유 포인터 인 경우 typedef를 전혀 제공하지 않지만 코드에서 shared_ptr 사용을 명시 적으로 유지하십시오.

실제로 템플릿 메타 프로그래밍 외부에서는 typedef를 거의 사용하지 않습니다.

STL은 항상 이런 유형의 일을합니다

멤버 함수와 중첩 된 typedefs로 정의 된 개념을 가진 STL 디자인은 역사적으로 막 다른 골목입니다. 현대 템플릿 라이브러리는 무료 함수와 특성 클래스를 사용합니다 (Bus.Graph 참조). 개념을 모델링하고 주어진 템플릿 라이브러리의 개념을 염두에두고 설계되지 않은 유형을보다 쉽게 ​​적용 할 수 있기 때문입니다.

STL을 같은 실수를 저지르는 이유로 사용하지 마십시오.


첫 번째 부분에 동의하지만 최근 편집 내용이 약간 짧습니다. 이러한 중첩 유형은 합리적인 기본값을 제공하므로 특성 클래스의 정의를 단순화합니다. 새 std::allocator_traits<Alloc>클래스를 고려하십시오. 작성하는 모든 단일 할당 자에 대해 특수화 할 필요는 없습니다. 단순히 유형을에서 직접 빌리기 때문입니다 Alloc.
Dennis Zickefoose의

@Dennis : C ++에서는 편의성이 라이브러리의 / author /가 아닌 라이브러리의 / user / 측에 있어야합니다. 사용자는 특성에 대해 균일 한 인터페이스를 원하고 특성 클래스 만 제공 할 수 있습니다. 위에 주어진 이유 때문에). 그러나 Alloc필자 조차도 std::allocator_traits<>필요한 typedef를 추가하는 것보다 새로운 유형 을 전문화하는 것이 어렵지 않습니다 . 전체 답변이 의견에 맞지 않기 때문에 답변을 수정했습니다.
Marc Mutz-mmutz

그러나 그것은 이다 사용자의 측면에. A와 사용자allocator_traits사용자 지정 할당을 만들려고, 내가 할 일은 말은 ... 특색 클래스의 다섯 명 멤버들과 귀찮게 할 필요가 없습니다typedef Blah value_type; 적절한 멤버 함수를 제공하고, 기본은 allocator_traits알아낼 것이다 쉬다. 또한 Boost.Graph의 예를 살펴보십시오. 예, 그것은 특성 클래스를 많이 사용합니다 ... 그러나 기본 자체 구현은 자체 내부 typedef에 대한 graph_traits<G>쿼리 G입니다.
Dennis Zickefoose

1
그리고 03 표준 라이브러리조차도 적절한 경우 특성 클래스를 사용합니다. 라이브러리의 철학은 컨테이너에서 일반적으로 작동하는 것이 아니라 반복자에서 작동하는 것입니다. 따라서 iterator_traits일반 알고리즘이 적절한 정보를 쉽게 쿼리 할 수 ​​있도록 클래스를 제공합니다 . 다시 기본적으로 반복자에게 자체 정보를 쿼리합니다. 그것의 길고 짧은 것은 특성과 내부 타입 정의가 상호 배타적이지 않다는 것입니다 ... 그들은 서로를 지원합니다.
Dennis Zickefoose

1
@Dennis : 의 모델이어야 iterator_traits하기 때문에 필요해 졌지만 필요한 typedef를에 넣을 수는 없습니다 . 일단 우리가 중첩 된 typedefs가 중복 되었기 때문에 중첩 된 typedef가 제거되기를 바랍니다. 같은 이유로 (내부 typedef를 추가 할 수 없음 ) STL 개념을 모델링하지 않으며 , 같은 kludge가 필요합니다 . Boost.Range는 중첩 된 typedef 나 멤버 함수가 필요하지 않기 때문에 모델링 할 수있는 최신 시퀀스 개념을 정의하는 방법을 보여줍니다 . T*RandomAccessIteratorT*iterator_traitsT[N]Sequencestd::array<T,N>T[N]
Marc Mutz-mmutz


8

Typdef는 확실히 좋은 스타일입니다. 그리고 당신의 모든 "내가 좋아하는 이유"는 좋고 정확합니다.

당신이 가진 문제에 대해. 앞으로의 선언은 성배가 아닙니다. 다중 레벨 종속성을 피하기 위해 코드를 간단히 디자인 할 수 있습니다.

typedef를 클래스 외부로 옮길 수는 있지만 Class :: ptr은 ClassPtr보다 훨씬 더 예쁘기 때문에이 작업을 수행하지 않습니다. 그것은 네임 스페이스와 비슷합니다-사물은 범위 내에서 연결되어 있습니다.

때때로 나는했다

Trait<Loren>::ptr
Trait<Loren>::collection
Trait<Loren>::map

또한 모든 도메인 클래스에 대해 기본값이 될 수 있으며 특정 도메인 클래스에 대한 일부 전문화가 가능합니다.


3

STL은 항상 이런 유형의 작업을 수행합니다. typedef는 STL의 많은 클래스에 대한 인터페이스의 일부입니다.

reference
iterator
size_type
value_type
etc...

다양한 STL 템플리트 클래스에 대한 인터페이스의 일부인 모든 typedef입니다.


사실, 나는 이것이 내가 처음 집어 든 곳이라고 생각합니다. 이것들은 정당화하기가 조금 더 쉬운 것처럼 보입니다. '메타 프로그래밍'라인을 따라 생각하면 클래스 템플릿 내에서 typedef를 변수와 더 유사하게 볼 수는 없습니다.
Will Baker

3

이것에 대한 또 다른 투표는 좋은 생각입니다. 시간과 공간 모두에서 효율적인 시뮬레이션을 작성할 때이 작업을 시작했습니다. 모든 값 유형에는 부스트 공유 포인터로 시작된 Ptr typedef가 있습니다. 그런 다음 프로파일 링을 수행 하고이 객체가 사용 된 코드를 변경하지 않고도 일부를 부스트 침입 포인터로 변경했습니다.

이것은 클래스가 사용될 위치를 알고 모든 용도가 동일한 요구 사항을 갖는 경우에만 작동합니다. 예를 들어 라이브러리를 사용할 때 컨텍스트를 작성할 때 알 수 없기 때문에 라이브러리 코드에서 이것을 사용하지 않습니다.


3

현재 저는 이러한 종류의 typedef를 집중적으로 사용하는 코드를 작성하고 있습니다. 지금까지는 괜찮습니다.

그러나 반복적 인 typedef가 자주 있고 정의가 여러 클래스로 나뉘어져 있으며 어떤 유형을 다루고 있는지 전혀 알지 못합니다. 내 임무는 이러한 typedef 뒤에 숨겨진 일부 복잡한 데이터 구조의 크기를 요약하는 것이므로 기존 인터페이스에 의존 할 수 없습니다. 3-6 단계의 중첩 네임 스페이스와 결합하면 혼란스러워집니다.

따라서 사용하기 전에 고려해야 할 몇 가지 사항이 있습니다.

  • 다른 사람이 이러한 typedef가 필요합니까? 수업은 다른 수업에서 많이 사용됩니까?
  • 사용법을 줄이거 나 수업을 숨기나요? (숨기는 경우 인터페이스도 생각할 수 있습니다.)
  • 다른 사람들이 코드를 사용하고 있습니까? 그들은 그걸 어떻게 햇어? 그들은 더 쉽다고 생각하거나 혼란스러워할까요?

1

이러한 typedef를 클래스 외부로 옮기는 것이 좋습니다. 이런 식으로 공유 포인터 및 벡터 클래스에 대한 직접적인 종속성을 제거하고 필요할 때만 포함시킬 수 있습니다. 클래스 구현에 이러한 유형을 사용하지 않는 한 내부 유형 정의가 아니어야한다고 생각합니다.

당신이 그것을 좋아하는 이유는 클래스 내에서 선언하지 않고 typedef를 통해 유형 별칭으로 해결되기 때문에 여전히 일치합니다.


익명의 네임 스페이스를 typedef로 오염시킬 수 있습니까?! typedef의 문제점은 실제 유형을 숨기므로 여러 모듈에 포함되거나 여러 모듈에 포함되어있을 때 찾기 / 수정하기 어려운 충돌을 일으킬 수 있다는 것입니다. 네임 스페이스 나 클래스 내부에 이들을 포함하는 것이 좋습니다.
Indy9000

3
이름 충돌과 익명의 네임 스페이스 오염은 클래스 내부 또는 외부에 형식 이름을 유지하는 것과 거의 관련이 없습니다. typedef가 아닌 클래스와 이름이 충돌 할 수 있습니다. 따라서 이름 오염을 피하려면 네임 스페이스를 사용하십시오. 네임 스페이스에서 클래스와 관련 typedef를 선언하십시오.
Cătălin Pitiș

typedef를 클래스에 넣는 또 다른 주장은 템플릿 함수를 사용하는 것입니다. 예를 들어 함수가 알 수없는 문자열 유형 (문자열 또는 고유 한 문자열 호환 변형)을 포함하는 알 수없는 컨테이너 유형 (벡터 또는 목록)을 수신 할 때. 컨테이너 페이로드의 유형을 파악하는 유일한 방법은 컨테이너 클래스 정의의 일부인 typedef 'value_type'을 사용하는 것입니다.
Marius

1

typedef가 클래스 자체 내에서만 사용되면 (즉, private으로 선언 됨) 좋은 생각이라고 생각합니다. 그러나 정확히 당신이주는 이유 때문에 typedef를 클래스 외부에서 알아야 할 경우에는 사용하지 않을 것입니다. 이 경우 수업 외부로 옮기는 것이 좋습니다.

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