Reputation: 141
I have a template class C<T>
which I intend to instantiate with T
as some other classes A
and B
. C<T>
has a method foo
whose signature I would like to depend on whether T
was instantiated as A
or B
. For example, consider the following code:
#include <iostream>
#include <string>
class A {
public:
void message() {
std::cout << "message with no args" << std::endl;
}
};
class B {
public:
void message(int x) {
std::cout << "message with " << x << std::endl;
}
};
template<typename T>
class C {
private:
T internal;
public:
C(T& x) {
internal = x;
}
void call() {
internal.message();
}
void call(int x) {
internal.message(x);
}
};
int main(int argc, char* argv[]) {
A a;
B b;
C<A> ca(a);
C<B> cb(b);
ca.call();
cb.call(42);
// ca.call(42); ERROR HERE
return 0;
}
This runs correctly. ca.call(42)
would raise a compilation error because there is no method A::message(int)
. However, if I for some reason introduce a method A::message(int)
in A
, the code may allow calling ca.call(42)
, which I would like to prevent.
I know that SFINAE techniques would allow to declare a method C::call(T::call_type x)
where T::call_type
would be a typedef for each intended instantiation of T
. However, this only allows me to change the type of the argument of C::call
. I would like instead to make the signature (in particular, the number of parameters) of C::call
on T
. I would thus prevent ca.call(42)
from being a valid call even if there is a method A::message(int)
in A
.
Is there any way to do this?
Upvotes: 1
Views: 263
Reputation: 1702
I don't know all the ins and outs of SFINAE, but what do you think of this?
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, A>::value>>
void call() {
internal.message();
}
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value>>
void call(int x) {
internal.message(x);
}
You can use == false
too
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value == false>>
Upvotes: 1
Reputation: 48998
You can do this with template specializations:
// Main template used by every other type T.
template<typename T>
class C; // or some other implementation.
// This gets used for T = A.
template<>
class C<A> {
private:
A internal;
public:
C(A& x) {
internal = x;
}
void call() {
internal.message();
}
};
// This gets used for T = B.
template<>
class C<B> {
private:
B internal;
public:
C(B& x) {
internal = x;
}
void call(int x) {
internal.message(x);
}
};
If you don't like that you need to duplicate some common code, then you can have a base class with all those common things and inherit from it in each specialization.
Upvotes: 0