Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the container type be deduced in variadic templates?

In C++11/C++14,

template <
   typename T ,
   template <typename...> class Container_t
>
void MyFunc(Container_t<T> &data) { ... }


template <typename T>
void MyFunc2( T v ) { ... }


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

   MyFunc<char, std::vector>(v);     // OK
   MyFunc(v);                        // error

   int i;

   MyFunc2<int>(i);                  // OK
   MyFunc2(i);                       // OK
}

I get an error with MyFunc(v).

Is it possible in any way to let the compiler find out the type of the container passed to the variadic template function? I can see no problems in finding it out, as with a normal types in normal templates.

If I need to change the type of v, do I have to fix all the calls to MyFunc?

Compiler: Microsoft Visual C++ 2015 (v140)

like image 837
Pietro Avatar asked Dec 05 '22 18:12

Pietro


2 Answers

Instead of trying to deduce container type, assume container defines what type it stores.

template <typename Container>
void MyFunc(Container& data)
{ 
   // all std containers defines value_type member (also reference etc.)
   using std::begin;
   using value_type = typename Container::value_type;
   value_type value = *begin(data);
  ...
}

Please note, that you might not need type of stored elements at all:

template <typename Container>
void MyFunc(Container& data)
{ 
   using std::begin;
   auto value = *begin(data);
  ...
}

if you want to work on std containers only (or the ones with similar templates arguments) - see Richard Hodges answer.

like image 106
Hcorg Avatar answered Dec 11 '22 11:12

Hcorg


The trick is to name the template's template arguments:

#include <vector>
#include <iostream>
#include <typeinfo>

template <
   typename T ,
   typename A,
   template <typename = T, typename = A> class Container_t
>
void MyFunc(Container_t<T, A> &data) { 
     std::cout << "value type = " << typeid(T).name() << std::endl;
     std::cout << "allocator type = " << typeid(A).name() << std::endl;
     std::cout << "container type = " << typeid(Container_t<T,A>).name() << std::endl;
   }


template <typename T>
void MyFunc2( T v ) {  }


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

   MyFunc<char, std::allocator<char>, std::vector>(v);     // OK
   MyFunc(v);                        // now ok

}

If you don't care about anything except the value type and the container...

#include <vector>
#include <map>
#include <iostream>
#include <typeinfo>

template <
   typename T ,
   typename...Rest,
   template <typename, typename...> class Container_t
>
void MyFunc(Container_t<T, Rest...> &data) { 
     std::cout << "value type = " << typeid(T).name() << std::endl;
     std::cout << "container type = " << typeid(Container_t<T,Rest...>).name() << std::endl;
   }


template <typename T>
void MyFunc2( T v ) {  }


int main()
{
   std::vector<char> v;
   std::map<char, int> m;

//   MyFunc<char, std::allocator<char>, std::vector>(v);     // OK
   MyFunc(v);                        // now ok
   MyFunc(m);                        // now ok

}
like image 42
Richard Hodges Avatar answered Dec 11 '22 09:12

Richard Hodges