I have a base
taking derived type as template parameter. The following code works as expected. instantiation of base<non_default_impl>
uses non_default_impl::data_t
and base<default_impl>
throws compilation error because event_data
is only a forward declaration.
template <typename T>
struct event_data;
template<typename T>
struct tovoid {
typedef void type;
};
template <typename T, typename enable = void>
struct get_data{
typedef event_data<T> type;
};
template <typename T>
struct get_data<T, typename tovoid<typename T::data_t>::type >{
typedef typename T::data_t type;
};
template <typename T>
struct base{
typedef typename get_data<T>::type data_type;
base(){
data_type();
}
};
struct non_default_impl{
struct data{};
typedef data data_t;
};
struct default_impl{
};
int main(){
base<non_default_impl> e1;
base<default_impl> e2;
return 0;
}
But when non_default_impl
inherits from base<non_default_impl>
SFINAE substitutes to the forward declaration.
struct non_default_impl: public base<non_default_impl>{
struct data{};
typedef data data_t;
};
int main(){
non_default_impl e1;
// base<default_impl> e2;
return 0;
}
prog.cpp: In instantiation of 'base::base() [with T = non_default_impl]':
prog.cpp:28:8: required from here
prog.cpp:24:3: error: invalid use of incomplete type 'base::data_type {aka struct event_data}' data_type();
How to make this work. I want if the derived class have a data_t
typedef use that otherwise use event_data<derived_type>
This is a caveat of CRTP : at the time your base
template is specialized for your non_default_impl
class, i.e in its base classes list, non_default_impl
itself is not defined yet.
Thus, any attempt to access anything that is part of its definition, for example the data_t
typedef, fails.
Since you cannot use anything that is inside non_default_impl
, a solution is to use an external type trait to choose your data_t
:
template <class T>
struct dataType { typedef event_data<T> type; };
template <typename T>
struct base{
typedef typename dataType<T>::type data_type;
// ...
};
// Usage
struct non_default_data {};
template <>
struct dataType<struct non_default_impl> {
typedef non_default_data type;
};
struct non_default_impl: public base<non_default_impl> {
// ...
};
Note that you can't declare non_default_data
inside non_default_impl
, since it must be accessible from the type trait, which must be accessible from the CRTP, which still has to be specialized before non_default_impl
is defined.
来源:https://stackoverflow.com/questions/36290887/mixing-crtp-with-sfinae