Как в классах C++ подставлять различные реализации методов на этапе компиляции?
Хотелось бы понять, как можно выбирать реализацию некоторого С++ метода, наподобии такого подхода:
#include <iostream>
class A {
public:
void foo()
{
std::cout << "foo A" << std::endl;
}
};
class B {
public:
void foo()
{
std::cout << "foo B" << std::endl;
}
};
template <typename T>
class C : public T {
};
int main () {
C<A> ca;
C<B> cb;
// Можно заметить, что вызов функций происходит напямую - без indirection call оверхеда.
// Никаких указателей на функции, значения которых будут разыменовываться в райнтайме
ca.foo(); // call 4011f0 <A::foo()>
cb.foo(); // call 40121c <B::foo()>
return 0;
}
Вопрос в следующем - может быть есть какой-нибудь способ на месте подставлять конкретные адреса вызова функций, но не используя шаблонных конструкций и/или наследования, при этом имея возмонжость легко переключаться между различными реализациями?
Например, вынести тип, из которого мы хотим извлекать адрес функции, используя псевдоним типа, что-то вроде:
#include <iostream>
class A {
public:
void foo()
{
std::cout << "foo A" << std::endl;
}
};
class B {
public:
void foo()
{
std::cout << "foo B" << std::endl;
}
};
class C {
public:
using T = B;
/* Тут и происходит магия, которая выполняет подстановку адреса функции */
};
int main () {
C cb;
cb.foo(); // call 40121c <B::foo()>
return 0;
}
Спасибо.
Ответы (1 шт):
Достаточно просто вызывать нужный метод из базового класса. Плюс проверка на то, что C наследуется от нужного типа. Но заметьте, что C<A,B> != C<B,A> (Да и вообще все варианты C<...> не будут одним и тем же классом), так что при таком стиле рано или поздно появятся сложности.
#include <utility>
#include <iostream>
struct A {
void foo() {
std::cout << "foo A" << std::endl;
}
};
struct B {
void foo() {
std::cout << "foo B" << std::endl;
}
};
template <typename ... Args>
struct C : public Args ... {
template <typename T> requires std::is_base_of_v<T, C>
void foo() {
T::foo();
}
};
int main() {
C<A, B> l;
l.foo<A>();
l.foo<B>();
}