Search code examples
c++c++11templatestemplate-templates

Templating a double-templated class method


This is my simplified code :

template<typename device, template<typename device> class protocol>
class MyClass
{
public:
  template<typename select>
  bool method()
  {
    // Code
  }
};

I want method to act different in terms of the protocol type.

In other words, I have two different possible protocols, and I want to have two different behaviours for my method according to the protocols. But I don't know how to write it with templates.


Solution

  • By example, using SFINAE (if you accept a C++11 solution)

    #include <iostream>
    #include <type_traits>
    
    template <typename>
    struct protocol_1
     { };
    
    template <typename>
    struct protocol_2
     { };
    
    template<typename device, template<typename> class protocol>
    class MyClass
     {
       public:
          template<typename select, typename p = protocol<device>>
             typename std::enable_if<
             std::is_same<p, protocol_1<device>>::value, bool>::type
             method()
           { return true; }
    
          template<typename select, typename p = protocol<device>>
             typename std::enable_if<
             std::is_same<p, protocol_2<device>>::value, bool>::type
             method()
           { return false; }
     };
    
    int main()
     {
       MyClass<int, protocol_1>  m1;
       MyClass<int, protocol_2>  m2;
    
       std::cout << m1.method<int>() << std::endl; // print 1 (true)
       std::cout << m2.method<void>() << std::endl; // print 0 (false)
     }
    

    --- EDIT ---

    As pointed by Yakk (thanks!), this solution is weak because use a template default value that can be explicited and circunvented.

    An example; with

    MyClass<int, protocol_1>{}.method<void>(); 
    

    is called the "protocol_1" version of method(), using the default value for p; but explciting p, as follows

    MyClass<int, protocol_1>{}.method<void, protocol_2<int>>(); 
    

    is called the "protocol_2" version of method() over an istance of a MyClass based on protocol_1

    To avoid this problem it's possible add a static_assert(), in both version of method(), to check and impose that p is equal to its default value (protocol<device>)

    I mean... as follow

      template<typename select, typename p = protocol<device>>
         typename std::enable_if<
            std::is_same<p, protocol_1<device>>::value, bool>::type
         method()
       {
         static_assert(std::is_same<p, protocol<device>>::value, "!");
    
         return true;
       }
    
      template<typename select, typename p = protocol<device>>
         typename std::enable_if<
         std::is_same<p, protocol_2<device>>::value, bool>::type
         method()
       {
         static_assert(std::is_same<p, protocol<device>>::value, "!");
    
         return false;
       }
    

    So

    MyClass<int, protocol_1>{}.method<void, protocol_2<int>>();
    

    generate a compiler error.