Question

How can I create a function which behaves like this? I can't seem to declare the return type correctly.

template <typename C, typename T0, typename T1>
typename C<T1> 
convert_container(const C<T0>& container, T1 value) {
  C<T1> new_container;
  // Do some stuff...
  return new_container;
}

std::vector<int> vec0;
const auto& vec1 = convert_container(vec0, float(2.0f)); // Produce a vector of floats

std::list<int> lst0;
const auto& lst1 = convert_container(lst0, float(2.0f)); // Produce a list of floats
Was it helpful?

Solution

The correct way is to use template template parameter:

  • C++11:

    template <template<typename...> class C, typename T0, typename T1>
    C<T1> convert_container(const C<T0>& container, T1 value) {
      C<T1> new_container;
      // Do some stuff...
      return new_container;
    }
    
  • C++03 (with allocator rebind):

    template <template<typename, typename> class C, typename T0, typename T1, typename Alloc>
    C<T1, typename Alloc::template rebind<T1>::other>
    convert_container(const C<T0, Alloc>& container, T1 value) {
      C<T1, typename Alloc::template rebind<T1>::other> new_container;
      // Do some stuff...
      return new_container;
    }
    
  • C++03 (without rebind):

    template <template<typename, typename> class C, typename T0, typename T1, typename Alloc>
    C<T1, std::allocator<T1> > convert_container(const C<T0, Alloc>& container, T1 value) {
      C<T1, std::allocator<T1> > new_container;
      // Do some stuff...
      return new_container;
    }
    

OTHER TIPS

The problem you're running into is that the way you're trying to use C, it isn't a simple type, but actually a class template. You want a template-template parameter:

template<template<typename> class C, typename T0, typename T1>
C<T1> convert_container( C<T0> const &container, T1 value ) {
    C<T1> new_container;
    // Do some stuff...
    return new_container;
}

Note that this won't typically work properly with standard containers such as vector, since they take more than one template argument (although some of them are defaulted). If you're working in C++11, you can do as @Jarod42 suggests and use template<typename...> instead of template<typename>.

Hmm, your version looks a bit unclean. I'd rather go with this approach:

#include <iterator>

template <class TargetContainer, class OriginalContainer>
TargetContainer convert_container(const OriginalContainer& inputContainer)
{
    TargetContainer result;

    auto inserter = std::inserter(result, result.begin());
    for (auto& item: inputContainer) {
        *inserter = item;
    }

    return result;
}

..which can be used like:

std::vector<int> vectorOfInts {1, 2, 3, 4};

auto vectorOfFloats = convert_container<std::vector<float>>(vectorOfInts);
auto listOfDoubles  = convert_container<std::list<double>>(vectorOfFloats);
auto setOfLongsWithCustomAlocator = convert_container<std::set<long, myComparator, myAlocator>>(...); //etc

If you don't want to use any C++11 just replace the range based for in convert_container with a normal for loop and the auto declaration with it's proper type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top