views:

1021

answers:

6

I need a template like this, which work perfectly

template <typename container> void mySuperTempalte (const container myCont)
{
    //do something here
}

then i want to specialize the above template for std::string so i came up with

template <typename container> void mySuperTempalte (const container<std::string> myCont)
{
    //check type of container
    //do something here
}

which doesnt't work, and throws an error. I would like to make the second example work and then IF possible i would like to add some code in the template to check if a std::vector/std::deque/std::list was used, to do something differently in each case. So i used templates because 99% of the code is the same for both vectors and deques etc.

+6  A: 

To specialize:

template<> void mySuperTempalte<std:string>(const std::string myCont)
{
    //check type of container
    //do something here
}

To specialize for vector:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

To specialize for deque:

template<typename C> void mySuperTempalte (std::deque<C> myCont)
{
    //check type of container
    //do something here
}
sep
This answers another question. ;-)
Konrad Rudolph
I think it does answer, even if not as directly and obvious as it could.
David Rodríguez - dribeas
Only the first is a specialization; the second and third cases are overloads instead.
MSalters
sep : You specialized for vector<C, allocator<C> > -- this code is better, than Konrad's, but will fail if the caller is using a custom allocator.
Aaron
I still maintain that this answer, while perhaps helpful, has got nothing to do with the question. It's not the *container* that needs to be specialized for, it's its `value_type`! This is a completely different problem and it requires a completely different solution.
Konrad Rudolph
+4  A: 

Have you tried a template typename parameter? The syntax is a bit weird because it emulates the syntax used to declare such a container. There's a good InformIT article explaining this in more detail.

template <template <typename> class Container>
void mySuperTemplate(Container<std::string> const& cont) {
}

Notice that you also should declare the argument as a reference!

By the way: this comment

//check type of container

is a dead giveaway that you're doing something wrong. You do not want to check the type of the container. User more sophisticated overloading instead, as shown in sep's answer.

Konrad Rudolph
Do _NOT_ use this code, as it will not work for STL containers. vector, list, etc are not single argument templates, they are templates taking at least two args, where all but the first have defaults (permitted an infinite number of extra args as implementation details). This code is NOT portable.
Aaron
@Aaron: true, thanks for pointing this out. But then, the way to do it is to use iterator arguments anyway instead of a container.
Konrad Rudolph
Konrad, good point. the OP probably should tell us what he *really* wants to do :)
Johannes Schaub - litb
+3  A: 

The answers so far seem helpful, but I think I'd use a different construct. I expect all containers to define value_type, just like the STL containers do. Therefore, I can write

inline template <typename C> void mySuperTemplate (C const& myCont)
{
    mySuperTemplateImpl<C, typename C::value_type>(myCont);
}

In general, it's easier to act on a parameter that you've extracted explicitly.

MSalters
+1  A: 

Whether it is a good design or not is left for further discussion. Anyway, you can detect the type of container using partial template specializations. In particular:

enum container_types
{
   unknown,
   list_container,
   vector_container
};

template <typename T>
struct detect_container_
{
   enum { type = unknown };
};

template <typename V>
struct detect_container_< std::vector<V> > // specialization
{
   enum { type = vector_container };
};

template <typename V>
struct detect_container_< std::list<V> >
{
   enum { type = list_container };
};

// Helper function to ease usage
template <typename T>
container_types detect_container( T const & )
{
   return static_cast<container_types>( detect_container_<T>::type );
}

int main()
{
   std::vector<int> v;

   assert( detect_container( v ) == vector_container );
}
David Rodríguez - dribeas
+2  A: 

@sep

'Simple' solution

The answer posted by 'sep' is pretty good, probably good enough for 99% of app developers, but could use some improvement if it's part of a library interface, to repeat:

To specialize for vector:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

This will work provided the caller isn't using std::vector. If this works well enough for you, to specialize for vector, list, etc, then stop here and just use that.

More complete solution

First, note that you can't partially specialize function templates -- you can create overloads. And if two or more of them match to the same degree, you will get "ambigous overload" errors. So we need to make exactly one match in every case you want to support.

One technique for doing this is using the enable_if technique -- enable_if allows you to selectively take function template overloads out of the possible match list using an obscure language rule... basically, if some boolean expression is false, the overload becomes 'invisible'. Look up SFINAE for more info if you're curious.

Example. This code can be compiled from the command line with MinGW (g++ parameterize.cpp) or VC9 (cl /EHsc parameterize.cpp) without error:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

template <bool B, class T> struct enable_if {};
template <class T> struct enable_if<true, T> { typedef T type; };

template <class T, class U> struct is_same { enum { value = false }; };
template <class T> struct is_same<T,T> { enum { value = true }; };

namespace detail{
    // our special function, not for strings
    //   use ... to make it the least-prefered overload
    template <class Container>
    void SpecialFunction_(const Container& c, ...){
        cout << "invoked SpecialFunction() default\n";
    }

    // our special function, first overload:
    template <class Container>
    // enable only if it is a container of mutable strings
    typename enable_if<
        is_same<typename Container::value_type, string>::value, 
        void
    >::type
    SpecialFunction_(const Container& c, void*){
        cout << "invoked SpecialFunction() for strings\n";
    }
}

// wrapper function
template <class Container>
void SpecialFunction(const Container& c){
    detail::SpecialFunction_(c, 0);
}

int main(){
    vector<int> vi;
    cout << "calling with vector<int>\n";
    SpecialFunction(vi);

    vector<string> vs;
    cout << "\ncalling with vector<string>\n";
    SpecialFunction(vs);
}

Output:

d:\scratch>parameterize.exe calling
with vector<int> invoked
SpecialFunction() default

calling with vector<string> invoked
SpecialFunction() for strings

d:\scratch>
Aaron
you've nailed it :) i was about to write a "fixed" version of my answer using boost::enable_if but then found out you need a whole lot of enable_if's if you have more than just special handling for string *and still generic support for other types*. but i didn't think of ellipsis trickery. +1! :)
Johannes Schaub - litb
+2  A: 

If I am understanding your problem correctly you have an algorithm that will work for STL containers vector, deque etc but are trying to write a template specialisation for string. If this is the case then you can write the generalised templated method that you defined in your question:-

template<typename container> void mySuperTempalte( const container &myCont )
{
    // Implement STL container code
}

Then for your string specialisation you declare:-

template<> void mySuperTempalte( const container<std::string> &myCont )
{
    // Implement the string code
}

For any other specialisation just change the type declaration for myCont. If you really need to do this for the vector and deque containers then make the template parameter the parameter for the type in that container rather than the container itself as Sep suggested.

template<typename C> void mySuperTempalte( const std::vector<C> &myCont)
{
    // check type of container
    // do something here
}

It's worth trying to avoid this by making your first implementation work with all STL containers to make your life easier, then you only need the specialisation for the string class. Even consider converting your string to a vector to avoid the specialisation all together.

On a side note, I've changed the container parameter to a const reference, I assume this is what you want, as you declare the object const anyway, this way you avoid a copy.

Matt