Introduction
Delegation, at least in the sense of this article, is a way to model a "is-implemented-in-terms-of" or IIITO object relationships between objects by forwarding functions to a member field. Typically delegation is achieved manually by explicitly writing out all of the functions. This is especially tedious for common interfaces.
Here is some source code which demonstrates how a class, FuBar, can forward the implementation of two separate interfaces, Fu and Bar, to two different fields, without introducing extra memory overhead.
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename Derived, typename Base>
Derived* ToDerived(Base* x) {
return static_cast<Derived*>(x);
}
template<typename Delegator>
struct FuDelegation {
int Fu() {
return
ToDerived<Delegator>(this)->Delegatee(this)->Fu();
}
};
template<typename Delegator>
struct BarDelegation {
int Bar() {
return
ToDerived<Delegator>(this)->Delegatee(this)->Bar();
}
};
struct FuStruct {
int Fu() { return 1; }
};
struct BarStruct {
int Bar() { return 2; }
};
struct FuBar :
FuDelegation<MyFuBar>, BarDelegation<MyFuBar> {
FuStruct* Delegatee(FuDelegation<MyFuBar>* x)
{ return &fu; }
BarStruct* Delegatee(BarDelegation<MyFuBar>* x)
{ return &bar; }
FuStruct fu;
BarStruct bar;
};
int main() {
MyFuBar fb;
cout << fb.Fu() << endl;
cout << fb.Bar() << endl;
system("pause");
}
What is required by FuBar to achieve the automated delegation is to inherit the two delegator structs, FuDelegation and BarDelegation. Notice that if your compiler implements the Empty Base Optimization (EBO) then this introduces no extra memory requirements to the class FuBar. Next the FuBar is required to provide member functions with the signature T* Delegatee(DelegationType* x) for each of the delegations, where T is a type which implements the appropriate interface.
Automated delegation can be used to implement what is sometimes called "dynamic inheritance", because the pointer returned by the Delegatee() function can be determined at runtime. All other techniques for automated delegation I know of introduce the overhead of at least one pointer, which is not strictly speaking required.
I am going to leave it as an exercise to the reader to figure out why the code works. I plan on explaining it in more detail in a future article if there is interest.
References