0

So I have a class that looks like this:

template <unsigned short MODE, unsigned int N = 128>
  class AESLocker {
  public:

    //Default Constructor
    AESLocker() : ENCRYPTION_KEY_LENGTH(N) {
      //Blah blah
    }

    //Contructor
    AESLocker(std::string key, std::string ivec);

And I am trying to implement different functions based on the MODE which are mentioned in an file with #defines.

However, I am failing at making two different constructors for 2 modes. Ie I want to partially specialize the constructor.

  //constructor
  template<unsigned short MODE, unsigned int N>
  AESLocker<MODE, N>::AESLocker(std::string key, std::string ivec)  {
    //blah
  }

The above template is currently used for all modes and all N. What I want is to specialize for MODE but not N. Ie I want a constructor for all N for mode 1 and another constructor for all N for mode 2.

I've spend the past 30 minutes trying various things. How do I do this?

Mo Beigi
  • 1,614
  • 4
  • 29
  • 50

1 Answers1

1

Constructors are special functions, but they are functions nonetheless. The language does not allow partial specialization of template functions, and thus you cannot partially specialize a constructor.

If the changes between the different versions happen in the body (rather than the initialization list), then you can move that logic to a different private function and call it using some other mechanism. Tag dispatch comes to mind:

  // Make these private
  template <unsigned short MODE, unsigned int N>
  void AESLocker<MODE, N>::init(std::string key, std::string ivec, Int2Type<1>) {
      // Specialization for MODE == 1
  }
  template <unsigned short MODE, unsigned int N>
  template <int X>
  void AESLocker<MODE, N>::init(std::string key, std::string ivec, Int2Type<X>) {
      // Generic for all other modes
  }
  // Dispatch based on MODE
  template<unsigned short MODE, unsigned int N>
  AESLocker<MODE, N>::AESLocker(std::string key, std::string ivec)  {
    init(std::move(key), std::move(N), Int2Type<MODE>());
  }

Note that I used the generic Int2Type, forcing the use of a templated init for the general case, but if you only want to distinguish 1 from the rest, you could use a tag type with just two options, 0 and 1 and dispatch with:

Int2Type<MODE == 1>()

In this case both overloads of init would be non-templated. Note that there is no practical difference other than the syntax overhead, since functions are generated on demand, which means that only one init will be generated per specialization of AESLocker.


As Jarod42 mentions, in C++11 and above you can also use forwarding constructors to do the same:

// private constructors
template <unsigned short MODE, unsigned int N>
AESLocker<MODE, N>::AESLocker(std::string & key, std::string & ivec, Int2Type<0>) 
: // initializer list
{ // constructor body
}
// public constructor
template <unsigned short MODE, unsigned int N>
AESLocker<MODE, N>::AESLocker(std::string key, std::string ivec)
: AESLocker(std::move(key), std::move(ivec), IntToType<MODE>())
{}

Where I have omitted one of the overloads. As an orthogonal change, I have modified the argument types to be referneces to strings (no need to keep moving the key and ivec objects around, we know the caller and we can do with the strings as we please).

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489