Reputation: 901
I am currently experimenting with templates of templates of stl containers
template <template <typename, typename > class Container, typename element, typename Allocator>
void printsize (Container<element, Allocator> & a){
std::cout<<"container size: "<<a.size()<<std::endl;
};
int main(){
std::vector<double> testvector;
for(int i = 0; i < 4; ++i){
testvector.push_back((double) i);
}
printsize(testvector);
std::list<std::vector<double> > testlist;
for(int i = 0; i < 8; ++i){
testlist.push_back(testvector);
}
printsize(testlist);
}
output correctly is
container size: 4
container size: 8
I am trying to limit the scope of template types the function accepts to something like
stl::container<double>.
If I try something like this:
template <template <typename, typename > class Container, typename Allocator>
void printsize (Container<double , Allocator> & a){
std::cout<<"container size: "<<a.size()<<std::endl;
};
will make the compiler complain about a
std::vector<int>
container as intended, however it does not work anymore on a
std::list<std::vector< double> >
because, well, double is a different type than std::vector
However, I want my function to act on arbitrary stl containers and stl containers of stl containers ( and so on ) of a specific "base" type only.
The reason behind this is that I do not want to use a lot of overloading. Basically what I am trying to achieve is writing some function
B dosomestuff(A a){
B b = someWorkDoneOnA(a);
return b;
}
stl::container<A> dosomestuff(stl::container<B> myContainer){
B newContainerElement;
stl::container<A> outputContainer;
for(auto i& : myContainer){
newContainerElement = dosomestuff(i);
outputContainer.push_back(newContainerElement);
}
return outputContainer;
};
By now I have a function that acts on vectors of B and creates vectors of A, overloaded with another function which acts on vectors of vectors of B and outputs vectors of vectors of A (calling the former function) and so on (honestly, for now that's it and I do not think I will need much more, but who knows?). It is literally the same piece of code (except for the type declarations), i.e. WET.
Upvotes: 1
Views: 63
Reputation: 63097
Lets start with a type trait for "N-Nested container of B's" (where N == 0 is B)
template <typename T>
struct is_B : std::false_type;
template <template <typename, typename > class Container, typename Element, typename Allocator>
struct is_B<Container<Element, Allocator>> : std::is_B<Element>;
template <>
struct is_B<B> : std::true_type;
You can then augment the working template with enable_if:
template <template <typename, typename > class Container, typename Element, typename Allocator>
void printThings(Container<std::enable_if<is_B<Element>::value, Element>, Allocator>) ...
This gets us halfway. We then make a collection type constructor template
template <typename T>
struct Bs_to_As {};
template <template <typename, typename > class Container, typename Element, typename Allocator>
struct Bs_to_As<Container<Element, Allocator>> { typedef Container<Bs_to_As<Element>::type, Allocator> type; }
template <>
struct Bs_to_As<B> { typedef A type; }
template <template <typename, typename > class Container, typename Element, typename Allocator>
Container<Bs_to_As<Element>, Allocator> doStuff(Container<Element, Allocator> myContainer)
{ ... }
Upvotes: 1