Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding member functions in a template class

Is it possible to hide some member functions in a template class? Let's imagine we have something like:

template <class T>
class Increment
{
public:
    void init(T initValue)
    {
         mValue = initValue;
    }  

    T increment()
    {
        ++mValue;
    }

    T increment(T delta)
    {
        mValue += delta;
    }
private:
    T mValue;
};

The objective is to use this class in a way that, in certain cases we only see the increment() function and in some other cases we only see the increment(T) member function. To do that, I can think about something with SFINAE:

class MultipleIncrement
{
    typedef int MultipleIncrement_t;
};

class SingleIncrement
{
    typedef int SingleIncrement_t;
};

template <class T, class Q>
class Increment
{
public:
    void init(T initValue)
    {
        mValue = initValue;
    }

    T increment(typename Q::SingleIncrement_t = 0)
    {
        ++mValue;
    }

    T increment(T delta, typename Q::MultipleIncrement_t = 0)
    {
        mValue += delta;
    }
private:
    T mValue;
}

And then use my template like, for example:

Increment<long, MultipleIncrement>

However, the compiler is not letting me do this. Is there any other way in which this is feasible? Would it also work if the member function is actually the constructor?

like image 204
Marda Avatar asked Apr 20 '11 19:04

Marda


3 Answers

In this case, I would prefer using template specialization. Would something like this help you?

struct SingleIncrement;
struct MultipleIncrement;

template <
    class T, 
    class Policy = SingleIncrement // default template param
>
class Increment
{
    T mValue;
public:
    Increment(T initValue)
    :   mValue(initValue)
    {}

    T increment()
    {
        ++mValue;
    }
};

// template specialization for MultipleIncrement
template <class T>
class Increment<T,MultipleIncrement>
{
    T mValue;
public:
    Increment(T initValue)
    :   mValue(initValue)
    {}

    T increment(T delta)
    {
        mValue += delta;
    }
};
like image 173
evnu Avatar answered Sep 28 '22 18:09

evnu


Template specialization is good. Inheritance sounds better. Have you considered templating on the inherited base class? (Or is this now considered a faux pax?)

#define SHOW(X)  cout << # X " = " << (X) << endl

template <class T>
class A
{
public:
  void foo(T t) {SHOW(t); }
};

template <class T, class BASE>
class B : public BASE
{
public:
  void bar(T t) {SHOW(t); }
};

int
main()
{
  B<int,A<int> > b;
  b.foo(1);
  b.bar(2);
}
like image 42
Mr.Ree Avatar answered Sep 28 '22 18:09

Mr.Ree


Here is a MWE of how this could be achieved:

#include <iostream>

using namespace std;

struct multi;
struct single;

template<class T, class Q>
struct dummy {
  dummy(T value) : m_value(value) { }

  // using enable_if_t in template argument
  template<class _Q = Q, enable_if_t<is_same<_Q, single>::value && is_same<_Q, Q>::value, int> = 1>
  T increment() { return ++m_value; }

  // using enable_if_t in method return type
  template<class _Q = Q>
  enable_if_t<is_same<_Q, multi>::value && is_same<_Q, Q>::value, T>
  //enable_if_t<is_same<_Q, multi>::value, T> // (*)
  increment(T delta) { return (m_value += delta); }

  T m_value;
};

int main() {
  dummy<double, multi> m(47.10);
  //cout << m.increment() << endl; // error as expected
  cout << m.increment(.01) << endl;

  dummy<int, single> s(41);
  cout << s.increment() << endl;
  cout << s.increment<single>() << endl;
  //cout << s.increment(1) << endl; // error as expected
  //cout << s.increment<multi>(1) << endl; // not an error when using (*)
}

Output

Using c++ (Debian 6.2.1-5) 6.2.1 20161124 this yields:

47.11
42
43

Elaboration

We need to template the methods to make SFINAE work at all. We cannot use something like

std::enable_if_t<std::is_same<_Q, multiple_increment>::value, T> increment() ...

because that fails when instantiating the template dummy<T, single_increment> instead of failing when substituting method template parameters.

Further, we want the user to be able to use the methods without actually providing a method template parameter. So we make the method template parameter _Q default to Q.

Finally, to really force a compiler error when using the unwanted method even when providing a method template parameter, we only enable_if_t the method if method template parameter _Q is actually the same type as the respective class template parameter Q.

like image 27
schlimmchen Avatar answered Sep 28 '22 19:09

schlimmchen