Is it possible to set different access modifiers to a class member template specializations (and a class template member ones)?

StackOverflow https://stackoverflow.com/questions/22537318

Question

  1. Is it possible to set different access modifiers to a class member template specializations? Example of code (does not compile):

    class SimpleClass
    {   
    public:
        template <typename T>
        void Method();
        template <>
        void Method<char>();
    protected:
        template <>
        void Method<int>();
    protected:
        template <>
        void Method<float>();
    };
    
    • Subquestion: is it possible to set different access modifiers to template constructor specializations of a class? Example of code (does not compile):

      class SimpleClass
      {
      public:
          template <typename T>
      SimpleClass(T);
          template <>
          SimpleClass<char>(char);
      protected:
          template <>
          SimpleClass<int>(int);
      private:
          template <>
          SimpleClass<float>(float);
      };
      
  2. Is it possible to set different access modifiers to a class template member specializations? Example of code (does not compile):

    template <typename T>
    class ClassTemplate
    {   
    public:
        void Method();
        template <>
        void Method<char>();
    protected:
        template <>
        void Method<int>();
    protected:
        template <>
        void Method<float>();
    };
    
    • Subquestion: is it possible to set different access modifiers to constructor specializations of a class template? Example of code (does not compile):

      template <typename T>
      class ClassTemplate
      {
      public:
          ClassTemplate(T);
          template <>
          ClassTemplate<char>(char);
      protected:
          template <>
          ClassTemplate<int>(int);
      private:
          template <>
          ClassTemplate<float>(float);
      };
      

To be more specific: in 1) I'm looking for a C++03 workaround for such C++11 code:

class SimpleClass
{
public:
    template <typename T>
    SimpleClass(T);

    template <typename T>
    void Method();
};

template <>
SimpleClass::SimpleClass<char>(char);
template <>
SimpleClass::SimpleClass<int>(int) = delete;

template <>
void SimpleClass::Method<char>();
template <>
void SimpleClass::Method<int>() = delete;

In 2) I'm looking for a C++03 workaround for the following C++11 code:

template <typename T>
class ClassTemplate
{
public:
    ClassTemplate(T);

    void Method();
};

template <>
ClassTemplate<char>::ClassTemplate(char);
template <>
ClassTemplate<int>::ClassTemplate(int) = delete;

template <>
void ClassTemplate<char>::Method();
template <>
void ClassTemplate<int>::Method() = delete;
Was it helpful?

Solution 2

Following some discussion, here is a complete account of all cases including different access specifiers using the approach of my previous answer.

template <bool C, typename T = void>
using only_if = typename std::enable_if <C, T>::type;

template <typename A, typename B>
using eq = typename std::is_same <A, B>::type;

class SimpleClass1
{
public:
    template <typename T, only_if <!eq <T, int>{} && !eq <T, float>{}, int> = 0>
    SimpleClass1() { }
protected:
    template <typename T, only_if <eq <T, int>{}, int> = 0>
    SimpleClass1() { }
protected:
    template <typename T, only_if <eq <T, float>{}, int> = 0>
    SimpleClass1() { }
};

class SimpleClass2
{
public:
    template <typename T, only_if <!eq <T, int>{} && !eq <T, float>{}, int> = 0>
    SimpleClass2(T) { }
protected:
    template <typename T, only_if <eq <T, int>{}, int> = 0>
    SimpleClass2(T) { }
private:
    template <typename T, only_if <eq <T, float>{}, int> = 0>
    SimpleClass2(T) { }
};

template <typename T>
class ClassTemplate1
{
public:
    template <typename U, only_if <!eq <U, int>{} && !eq <U, float>{}, int> = 0>
    void Method() { }
protected:
    template <typename U, only_if <eq <U, int>{}, int> = 0>
    void Method() { }
protected:
    template <typename U, only_if <eq <U, float>{}, int> = 0>
    void Method() { }
};

template <typename T>
class ClassTemplate2
{
public:
    template <typename U, only_if <!eq <U, int>{} && !eq <U, float>{}, int> = 0>
    void Method(U) { }
protected:
    template <typename U, only_if <eq <U, int>{}, int> = 0>
    void Method(U) { }
protected:
    template <typename U, only_if <eq <U, float>{}, int> = 0>
    void Method(U) { }
};

I don't know where all this would be useful :-) Anyhow:

  • I have been careful to make all constructor/method overloads mutually exclusive to avoid ambguities and problems with different access specifiers, which can be tricky. This makes it more difficult to generalize to more types. A template alias would help for the generic/default case (which is the complement of all others).

  • However, this is not exactly equivalent to what you described in the question. These methods enforce strict type equality, so do not allow implicit conversions. You could try std::is_convertible instead but then you're opening the door to ambiguities.

  • The entire code compiles as such but I didn't try to actually use the classes, so I don't know what might happen.

  • I really don't know how one can use SimpleClass1: How can we possibly explicitly specify a template argument for a default constructor (since it cannot be deduced)?

  • Looking at the code again, I think that ClassTemplate is not much different (or at all) from SimpleClass. ClassTemplate1 cannot have a default template argument, because that would be ambiguous.

OTHER TIPS

We can do this with SFINAE, but unfortunately we need is_same and enable_if. Fortunately, neither of those require any C++11 language! So we can just bring in their implementations for our own needs:

template <typename A, typename B>
struct is_same {
  static const bool value = false;
};

template<typename A>
struct is_same <A, A> {
  static const bool value = true;
};

template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };

Next, we combine them using enable_if to "SFINAE delete" methods you don't want. The template constructor/method needs a gimmick. We need the first argument to be the type for argument deduction. We add a second, dummy argument for the sole purpose of deleting the method if the type isn't one you support. You can add overloads for more types if you need to. For Method() we can apply the dummy argument again, and T will be given explicitly, so no need for any deduction:

class SimpleClassConstructor {
public:
  template <typename T>
  SimpleClassConstructor(T value, 
                         typename enable_if<is_same<T, char>::value, T>::type enabler = 0) {
  }
  template <typename T>
  void Method(typename enable_if<is_same<T, char>::value, T>::type enabler = 0) {
  }
};

For the template class, ~~we can use the exact same methodology, but we can use the result of enable_if directly since we don't need argument deduction.~~ To satisfy the requirements of specifically deleting methods, we can move the enable_if methodology to a template parameter. This will let SFINAE delete the method softly (rather than disabling the entire class if the enable_if fails):

template <typename T>
class SimpleClassTemplate {
public:
  template <typename Enable = enable_if<is_same<T, char>::value, T>::type>
  SimpleClassTemplate(T value) {

  }
};

For a test, try these:

int main() {
  char a = 0;
  SimpleClassTemplate<char> A1(a); // OK
  SimpleClassConstructor A2(a); // OK

  A2.Method<char>(); // OK
  A2.Method<int>(); // compilation error!

  int b = 0;
  SimpleClassTemplate<int> B1(b); // compilaton error!
  SimpleClassConstructor B2(b); // compilation error!
}

For a default constructor or method with no input parameters, I think the answer to Template specialization within class definition suffices. It only uses std::enable_if and std::is_same, which are easy to define.

For an one-parameter constructor or method, I think you can apply the same method, which is even easier here because there's already a (deduced) template parameter so you don't need a dummy one.

template <bool C, typename T = void>
using only_if = typename std::enable_if <C, T>::type;

template <typename A, typename B>
using eq = typename std::is_same <A, B>::type;

class SimpleClass {
public:
    template <typename T, only_if <!eq <T, int>{}, int> = 0>
    SimpleClass(T) { ... }

    template <typename T, only_if <!eq <T, int>{}, int> = 0>
    void Method(T) { ... }
    // ...
};

Try to use "Template Specialization":

template <typename T>
struct S {
    void foo();
    void bar();
};

template <>
struct S<int> {
    void foo();
};



int main()
{
    S<char> sc;
    sc.foo();
    sc.bar();

    S<int> si;
    si.foo();
    si.bar(); // compile error: 'bar' : is not a member of 'S<int>'

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