Search code examples
c++templatestemplate-templates

How to specify shared template variable with two template template parameters?


I'm trying to build a mini test framework. I have one function that takes in two List-like variables that are composed of the same type and I plan on using template template parameters as part of the interface. So far, I have this,

    template <typename C, template <class> class A, template <class> class B>
    static inline void assertEquals(const A<C>& expected, const B<C>& actual) {
      auto success = 0, failure = 0;

      for (auto iter1 = expected.cbegin(), iter2 = actual.cbegin();
           iter1 != expected.cend() && iter2 != actual.cend(); ++iter1, ++iter2) {
        if (Test::assertEquals<C>(*iter1, *iter2)) {
          ++success;
        } else {
          ++failure;
        }
      }

      cout << "Success: " << success << endl
           << "Failure: " << failure << endl;
    }

The assertEquals in the if condition is another function. My question is, is the interface correct? Secondly, how would I use it? I've tried this to no avail,

    Test::assertEquals<int, std::vector, std::vector>(haystack, needle);

Test:: is simply the class that the function resides in and haystack and needle are of type std::vector<int>.


Solution

  • Your interface won't accept std::vector, because it actually takes two template arguments: the contained type and an allocator.

    You could change your template signature to accept variadic template templates, like this:

    template <typename C, template <class...> class A, template <class...> class B>
    static inline void assertEquals(const A<C>& expected, const B<C>& actual) {
    

    Or you could set the default parameter explicitly:

    template <typename C, 
              template <class, class = std::allocator<C>> class A, 
              template <class, class = std::allocator<C>> class B>
        void assertEquals(const A<C>& expected, const B<C>& actual) {
    

    However, you're probably better off just getting C from the STL container member types, like this:

    template <class A, class B>
    void assertEquals(const A& expected, const B& actual) {
        static_assert(std::is_same<typename A::value_type, typename B::value_type>::value,
                      "Containers must have the same value type");
        using C = typename A::value_type;
        //...
    

    This is a much more simple interface. For all of the above options, the compiler can deduce the template arguments for you, so just call it like:

    assertEquals(haystack, needle);