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?
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;
}
};
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);
}
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 (*)
}
Using c++ (Debian 6.2.1-5) 6.2.1 20161124
this yields:
47.11
42
43
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With