GuyGizmo GuyGizmo - 7 days ago 5
C++ Question

In C++11 is there anyway to get the general template class from a template class with specified template arguments?

(e.g. get

std::list
from
std::list<some_value_type>
)


Consider the following code:

#include <list>
#include <string>

std::string combine(int val, std::string str)
{
return std::to_string(val) + str;
}

template <class T, class U>
auto foo(const T &container, const U &val)
-> std::list<U>
{
using RetType = std::list<U>;
RetType result;

for(auto item : container) {
result.push_back(combine(item, val));
}

return result;
}

int main()
{
std::list<int> numbers = {1, 2, 3, 4, 5};
std::list<std::string> result = foo(numbers, std::string(" potato"));
return 0;
}


This compiles, but I want it to work differently. I'd like
foo
to return the same kind of container as was passed into its first argument, but with its value type changed to that of the second argument, i.e. type
U
.

So if
foo
is passed in
std::list<int>
as its first argument and
std::string
as its second argument, it returns
std::list<std::string>
. Alternatively, if
foo
is passed in
std::vector<int>
as its first argument and
std::string
as its second argument, it returns
std:: vector<std::string>
. And so on.

Basically I want to replace both instances of
std::list<U>
with something that accomplishes the above, possibly using the facilities of
<type_traits>
.

Is there anyway to do this in C++11? The only solution I've found is to create overloaded versions of
foo
for every container type I want to use, but I'd much prefer if there was a general way to do it that covered all container types.

Answer

Yes, you can use a template-template parameter and change every occurrence of list<T> to a generic C<T>:

template <template<class...>class C, class T, class U>
auto 
foo(const C<T> &container, const U &val) -> C<U>
{
  using RetType = C<T>;
  // ...
}

An alternative which is commonly used is to use iterators which are a generic interface into the elements of a container. The standard algorithm that can be used is std::transform:

transform(numbers.begin(), numbers.end(), inserter(result, result.begin()), [] (int n) {
  return combine(n, " potato");
});