0

Consider the following program.

#include <vector>
#include <unordered_set>

/**
 * Convert any templated iterable container to a std::unordered_set.
 */
template <typename T>
std::unordered_set<T> vectorToUnorderedSet(const std::vector<T>& container) {
    std::unordered_set<T> set;
    for (T t: container) {
        set.insert(t);
    }
    return set;
}

int main(){
    std::vector<int> vec {1,2,3};
    std::unordered_set<int> set = vectorToUnorderedSet(vec);
}

If I compile it with the following command, it compiles and runs fine.

g++ -std=c++11 -O3 -Wall -g TemplatePlay.cc -o TemplatePlay -pthread

Now, I'd like to generalize this function, so that it can take any templated container as input.

Here is my first attempt.

/**
 * Convert any templated iterable container to a std::unordered_set.
 */
template <typename T, typename C>
std::unordered_set<T> containerToUnorderedSet(const C<T>& container) {
    std::unordered_set<T> set;
    for (T t: container) {
        set.insert(t);
    }
    return set;
}

This fails with the following error.

g++ -std=c++11 -O3 -Wall -g TemplatePlay.cc -o TemplatePlay -pthread
TemplatePlay.cc:8:51: error: ‘C’ is not a template
 std::unordered_set<T> containerToUnorderedSet(const C<T>& container) {
                                                   ^
TemplatePlay.cc: In function ‘int main()’:
TemplatePlay.cc:18:60: error: no matching function for call to ‘containerToUnorderedSet(std::vector<int>&)’
     std::unordered_set<int> set = containerToUnorderedSet(vec);
                                                            ^
TemplatePlay.cc:8:23: note: candidate: template<class T, class C> std::unordered_set<T> containerToUnorderedSet(const C&)
 std::unordered_set<T> containerToUnorderedSet(const C<T>& container) {
                       ^~~~~~~~~~~~~~~~~~~~~
TemplatePlay.cc:8:23: note:   template argument deduction/substitution failed:
TemplatePlay.cc:18:60: note:   couldn't deduce template parameter ‘T’
     std::unordered_set<int> set = containerToUnorderedSet(vec);

                                                        ^

Next, I tried the magic incantations mentioned in this answer, but I don't understand the syntax, so it's possible that I implemented it incorrectly.

/**
 * Convert any templated iterable container to a std::unordered_set.
 */
template <typename T, template <typename> class C>
std::unordered_set<T> containerToUnorderedSet(const C<T>& container) {
    std::unordered_set<T> set;
    for (T t: container) {
        set.insert(t);
    }
    return set;
}

This resulted in the following error.

g++ -std=c++11 -O3 -Wall -g TemplatePlay.cc -o TemplatePlay -pthread
TemplatePlay.cc: In function ‘int main()’:
TemplatePlay.cc:18:60: error: no matching function for call to ‘containerToUnorderedSet(std::vector<int>&)’
     std::unordered_set<int> set = containerToUnorderedSet(vec);
                                                            ^
TemplatePlay.cc:8:23: note: candidate: template<class T, template<class> class C> std::unordered_set<T> containerToUnorderedSet(const C<T>&)
 std::unordered_set<T> containerToUnorderedSet(const C<T>& container) {
                       ^~~~~~~~~~~~~~~~~~~~~
TemplatePlay.cc:8:23: note:   template argument deduction/substitution failed:
TemplatePlay.cc:18:60: note:   template parameters of a template template argument are inconsistent with other deduced template arguments
     std::unordered_set<int> set = containerToUnorderedSet(vec);
                                                            ^
Makefile:8: recipe for target 'TemplatePlay' failed
make: *** [TemplatePlay] Error 1

If possible, what are the correct incantations for defining a templated method which takes a type requiring a template parameter?

The accepted answer to the question claimed as a duplicate does not address these compiler errors.

merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • You’re looking for **template template parameters**. – Pete Becker Jul 09 '18 at 23:46
  • 1
    A simpler solution might be this: There is the `unordered_set` constructor which takes two iterators: `template< class InputIt > unordered_set( InputIt first, InputIt last, ...);` Perhaps you could let `C` parameterize the complete type of the container in question, then when given some container `const C& container`, call its `container.begin()` and `container.end()` members. – alter_igel Jul 09 '18 at 23:48
  • 1
    A `std::vector` isn't merely `C`. Check https://godbolt.org/g/wsyR7Q – DeiDei Jul 09 '18 at 23:48
  • @PeteBecker, Use of the accepted answer to the duplicate question still results in the compiler errors I reported. – merlin2011 Jul 09 '18 at 23:50
  • Standard containers have a `value_type` defined, which you can utilize in your template code, eg: `template std::unordered_set containerToUnorderedSet(const Container &c) { std::unordered_set s; for (auto &v: c) { s.insert(v); } return s; }` Or even simpler: `template std::unordered_set containerToUnorderedSet(const Container &c) { return std::unordered_set(std::begin(c), std::end(c)); }` – Remy Lebeau Jul 10 '18 at 08:12

0 Answers0