C ++ CRTP (정적 다형성) 및 파생 클래스의 typedef 사용
나는 읽기 위키 백과 문서 다형성 : 정적 (컴파일시 읽기)을 수행하는 C의 호기심 반복 템플릿 패턴 ++에 대한합니다. 파생 된 형식에 따라 함수의 반환 형식을 변경할 수 있도록 일반화하고 싶었습니다. (기본 유형이 템플릿 매개 변수에서 파생 된 유형을 알고 있기 때문에 가능한 것 같습니다.) 불행히도 다음 코드는 MSVC 2010을 사용하여 컴파일되지 않습니다 (지금은 gcc에 쉽게 액세스 할 수 없으므로 아직 시도하지 않았습니다). 왜 그럴까요?
template <typename derived_t>
class base {
public:
typedef typename derived_t::value_type value_type;
value_type foo() {
return static_cast<derived_t*>(this)->foo();
}
};
template <typename T>
class derived : public base<derived<T> > {
public:
typedef T value_type;
value_type foo() {
return T(); //return some T object (assumes T is default constructable)
}
};
int main() {
derived<int> a;
}
BTW, 추가 템플릿 매개 변수를 사용하는 해결 방법이 있지만 마음에 들지 않습니다. 상속 체인에 많은 유형을 전달할 때 매우 장황해질 것입니다.
template <typename derived_t, typename value_type>
class base { ... };
template <typename T>
class derived : public base<derived<T>,T> { ... };
편집하다:
이 상황에서 MSVC 2010이 제공하는 오류 메시지는 다음과 같습니다. error C2039: 'value_type' : is not a member of 'derived<T>'
g ++ 4.1.2 ( codepad.org 를 통해 )는 말합니다.error: no type named 'value_type' in 'class derived<int>'
derived
base
기본 클래스 목록에서 템플릿 인수로 사용할 때 불완전 합니다.
일반적인 해결 방법은 특성 클래스 템플릿을 사용하는 것입니다. 여기에 귀하의 예가 있습니다. 이것은 트레이 트를 통해 파생 클래스의 형식과 함수를 모두 사용할 수있는 방법을 보여줍니다.
// Declare a base_traits traits class template:
template <typename derived_t>
struct base_traits;
// Define the base class that uses the traits:
template <typename derived_t>
struct base {
typedef typename base_traits<derived_t>::value_type value_type;
value_type base_foo() {
return base_traits<derived_t>::call_foo(static_cast<derived_t*>(this));
}
};
// Define the derived class; it can use the traits too:
template <typename T>
struct derived : base<derived<T> > {
typedef typename base_traits<derived>::value_type value_type;
value_type derived_foo() {
return value_type();
}
};
// Declare and define a base_traits specialization for derived:
template <typename T>
struct base_traits<derived<T> > {
typedef T value_type;
static value_type call_foo(derived<T>* x) {
return x->derived_foo();
}
};
base_traits
템플릿 인수 derived_t
에 사용하는 모든 유형을 base
전문화하고 각 전문화가 필요한 모든 멤버를 제공하는지 확인 하기 만하면 base
됩니다.
트레이 트 사용의 한 가지 작은 단점은 각 파생 클래스에 대해 하나씩 선언해야한다는 것입니다. 다음과 같이 덜 장황하고 중복되는 해결 방법을 작성할 수 있습니다.
template <template <typename> class Derived, typename T>
class base {
public:
typedef T value_type;
value_type foo() {
return static_cast<Derived<T>*>(this)->foo();
}
};
template <typename T>
class Derived : public base<Derived, T> {
public:
typedef T value_type;
value_type foo() {
return T(); //return some T object (assumes T is default constructable)
}
};
int main() {
Derived<int> a;
}
C ++ 14 typedef
에서는 함수 auto
반환 유형 추론을 제거 하고 사용할 수 있습니다 .
template <typename derived_t>
class base {
public:
auto foo() {
return static_cast<derived_t*>(this)->foo();
}
};
This works because the deduction of the return type of base::foo
is delayed until derived_t
is complete.
An alternative to type traits that requires less boilerplate is to nest your derived class inside a wrapper class that holds your typedefs (or using's) and pass the wrapper as a template argument to your base class.
template <typename Outer>
struct base {
using derived = typename Outer::derived;
using value_type = typename Outer::value_type;
value_type base_func(int x) {
return static_cast<derived *>(this)->derived_func(x);
}
};
// outer holds our typedefs, derived does the rest
template <typename T>
struct outer {
using value_type = T;
struct derived : public base<outer> { // outer is now complete
value_type derived_func(int x) { return 5 * x; }
};
};
// If you want you can give it a better name
template <typename T>
using NicerName = typename outer<T>::derived;
int main() {
NicerName<long long> obj;
return obj.base_func(5);
}
I know that this is basically the workaround you found and don't like, but I wanted to document it and also to say that it is basically the current solution to this problem.
I have been looking for a way to do this for a while and never found a good solution. The fact that it is not possible is the reason why ultimately, things like boost::iterator_facade<Self, different_type, value_type, ...>
need many parameters.
Of course we would like something something like this to work:
template<class CRTP>
struct incrementable{
void operator++(){static_cast<CRTP&>(*this).increment();}
using ptr_type = typename CRTP::value_type*; // doesn't work, A is incomplete
};
template<class T>
struct A : incrementable<A<T>>{
void increment(){}
using value_type = T;
value_type f() const{return value_type{};}
};
int main(){A<double> a; ++a;}
If this was possible, all the traits of the derived class could be passed implicitly ot the base class. The idiom I found to get the same effect is to pass the traits to the base class entirely.
template<class CRTP, class ValueType>
struct incrementable{
void operator++(){static_cast<CRTP&>(*this).increment();}
using value_type = ValueType;
using ptr_type = value_type*;
};
template<class T>
struct A : incrementable<A<T>, T>{
void increment(){}
typename A::value_type f() const{return typename A::value_type{};}
// using value_type = typename A::value_type;
// value_type f() const{return value_type{};}
};
int main(){A<double> a; ++a;}
The drawback is that the trait in the derived class has to be accessed with a qualified typename
or reenabled by using
.
'Development Tip' 카테고리의 다른 글
[UILabel copyWithZone :] : 인식 할 수없는 선택기가 인스턴스로 전송되었습니다. (0) | 2020.12.13 |
---|---|
XML에서 JAXB 클래스를 생성하는 방법 (0) | 2020.12.12 |
HotSpot JVM에서 삭제되는 압축 문자열 지원? (0) | 2020.12.12 |
탐지기, 추출기 및 매처 분류 (0) | 2020.12.12 |
cgroup과 네임 스페이스의 차이점 (0) | 2020.12.12 |