This is a base Repository
class interface, which has a iterator()
method. Just like the design pattern URL, the Repository
classes and Iterator
class are both polymorphism. Instead of virtual function and pimpl, i choose to use CRTP here.
template <typename Impl>
struct RepositoryTraits;
template <typename Impl>
class Repository{
public:
template<typename ConcreteIterType>
struct Iterator{
int next(){
return static_cast<ConcreteIterType*>(this)->next();
}
};
Iterator<typename RepositoryTraits<Impl>::IteratorType> iterator(){
return static_cast<Impl*>(this)->iterator();
}
};
Inspired by this wonderful answer. I try to use a trait helper to retrieve the actual IterType
From derived Repository
class instead of add another template parameter in the Repository
's template parameter list:
class RepositoryImpl : public Repository<RepositoryImpl>{
public:
struct RealIterator : public Iterator<RealIterator>{
int next(){
return 1;
}
};
using IteratorType = RealIterator;
RealIterator iterator(){
return RealIterator();
}
};
template <>
struct RepositoryTraits<RepositoryImpl> {
using IteratorType = RepositoryImpl::IteratorType;
};
Of course, the above code cannot compile. The compiler hint me "implicit instantiation of undefined template 'RepositoryTraits"
I've found this question, but i cannot learn from it. What is the correct way to combine CRTP and type traits here? Or should i use a better design? Any help will be appreciated.
I've found some trait based answers, like http://www.cplusplus.com/forum/general/266019/ or C++ static polymorphism (CRTP) and using typedefs from derived classes. But I cannot figure out why it can succeed in these case while not in mine.
I've tried to directly return a T
and use SFINAE to restrict its type in the base class like below. The code cannot work either, since it force the caller to provide a T
for the call of iterator()
.
Changing iterator
to auto
deduction return type can work. However, i still want to use SFINAE to fix the Iterator
type in subclass. How can this be done?
#include <type_traits>
#include <iostream>
template <typename Impl>
class Repository{
public:
template<typename ConcreteIterType>
struct Iterator{
int next(){
return static_cast<ConcreteIterType*>(this)->next();
}
};
template <typename T>
std::enable_if_t<std::is_base_of<Iterator<T>, T>::value, T> iterator(){
return static_cast<Impl*>(this)->iterator();
}
};
class RepositoryImpl : public Repository<RepositoryImpl>{
public:
struct RealIterator : public Iterator<RealIterator>{
int next(){
return 1;
}
};
using IteratorType = RealIterator;
RealIterator iterator(){
return RealIterator();
}
};