1
class trytemplate
{
public:
    //////// 1
    template <typename T>
    trytemplate(const T& p)
    {
        std::cout << p << std::endl;
    }

    //////// 2
    template <typename U>
    trytemplate(const std::vector<U>& p)
    {
        std::cout << p[0] << " " << p.size() << std::endl;
    }

    //////// 3
    template <typename U, typename V>
    trytemplate(const V<U>& p)
    {
        std::cout << p[0] << " " << p.size() << std::endl;
    }
};

ctor 2 works fine, but I'd like to make it something like 3 (3 doesnt compile).
So that I can do something like:

int i = 123;
trytemplate o1(i); // call ctor 1

std::vector<float> v1(1, 123.0f);
trytemplate o2(v1); // ideally can be deduced by compiler, and call ctor 3

MyVector<float> v2(1, 123.0f);
trytemplate o3(v2); // ideally can be deduced by compiler, and call ctor 3

In such case I can pass in any vector-like container, just making sure that class has operator[] and size().

So the question is: is it possible to make ctor like number 3?
Or is there any better approach?

P.S. If anyone could suggest a better title then I would change it, thanks!

Marson Mao
  • 2,935
  • 6
  • 30
  • 45
  • 2
    If I understand the question correctly, you are talking about [template template parameters](http://stackoverflow.com/questions/213761/what-are-some-uses-of-template-template-parameters-in-c). – Cody Gray - on strike Jun 19 '14 at 06:21
  • Why do you think `std::vector v1(1, 123.0f);` should call the constructor 3 instead of constructor 2? – R Sahu Jun 19 '14 at 06:27
  • If 3 works then I would delete 2, sorry didnt make that clear. – Marson Mao Jun 19 '14 at 06:29
  • 1
    @MarsonMao You don't need to get rid of 2, but you should know that if it exists, and you pass a `vector` to `trytemplate`, constructor (2) will always be selected because it's a better match than (3) – Praetorian Jun 19 '14 at 06:32
  • @Praetorian I know it, thanks. ctor2 is only here for explanation, ctor3 is actually what I wanted. template template parameter is great, should be what i need, but there are some compile errors, seems that my compiler(vs2012) thinks ctor1 is a better match for `std::vector`?! – Marson Mao Jun 19 '14 at 06:36
  • Why do you think you need this? `p[0]` and `p.size()` will work just fine in version 1. Are you trying to do something with the same container template and a different element type, like how `std::allocator::rebind` works? – Ben Voigt Jun 19 '14 at 06:56
  • actually i want to distinguish native type, vector-like, and map-like classes. – Marson Mao Jun 19 '14 at 07:28

1 Answers1

4

Use a template template parameter

template <template<typename> class V, typename U>
trytemplate(const V<U>& p)
{
    std::cout << p[0] << " " << p.size() << std::endl;
}

You can also add in variadic templates to accept class templates that take more than one template parameters.

template <template<typename...> class V, typename... Params>
trytemplate(const V<Params...>& p)
{
    std::cout << p[0] << " " << p.size() << std::endl;
}

Note that if you use the non-variadic (first) version then the class template you pass in should only take a single template argument. This means it cannot be used with std::vector because it takes a second template argument, the allocator type (which has a default argument of std::allocator<T>). If your compiler doesn't support variadic templates, like VS2012 without Nov CTP, then use this

template <template<typename, typename> class V, typename U, typename A>
trytemplate(const V<U, A>& p)
{
    std::cout << p[0] << " " << p.size() << std::endl;
}
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Hi, I'm trying template template parameter but it gives me compile error, weird. – Marson Mao Jun 19 '14 at 06:30
  • error C2039: 'size' : is not a member of 'std::_Vector_val<_Val_types>' and error C2676: binary '[' : 'const std::_Vector_val<_Val_types>' does not define this operator or a conversion to a type acceptable to the predefined operator – Marson Mao Jun 19 '14 at 06:31
  • I got the above errors if I comment ctor1 and only try to instantiate `trytemplate o2(v1)`. If I keep both ctor1 and ctor3, then another error occurs: error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const std::vector<_Ty>' (or there is no acceptable conversion). Seems that compiler is trying to use ctor1 instead of ctor3. – Marson Mao Jun 19 '14 at 06:34
  • @MarsonMao [This code](http://coliru.stacked-crooked.com/a/3dd6f8da529653db) compiles and produces the correct output on VS2013 – Praetorian Jun 19 '14 at 06:35
  • do you have vs2012 (my compiler right now)? could it be the problem of vs2012?! – Marson Mao Jun 19 '14 at 06:37
  • @MarsonMao I think I know the problem. You're using the non-variadic version, right? – Praetorian Jun 19 '14 at 06:38
  • Yes, vs2012 cant support varaidic (sucks, lol), so I'm trying the first version you provided. If I keep both ctor1 and ctor3(fixed) and try to instantiate `o2(v1)` then there is C2679. If I comment ctor1, then errors become C2676 and C2039. – Marson Mao Jun 19 '14 at 06:41
  • @MarsonMao See edit, that should fix it. But it does make the solution rather limited. – Praetorian Jun 19 '14 at 06:42
  • It works!! That's it! Thank you for fast and clear answer! (I'll try to upgrade to 2013...lol) – Marson Mao Jun 19 '14 at 06:45