'CallbackList', 'EventDispatcher', and 'EventQueue' are homogeneous. All listeners must have the same prototype. For example,
```c++
eventpp::EventDispatcher<int,void()> dispatcher;
dispatcher.appendListener(3, []() {}); // OK
dispatcher.appendListener(3, [](std::string) {}); // wrong, can't listen for void(std::string)
```
There are heterogeneous counterparts, 'HeterCallbackList', 'HeterEventDispatcher', and 'HeterEventQueue'. These classes allow listeners having different prototypes. For example,
The only difference is the `Prototype` in homo-classes becomes `PrototypeList` in heter-classes.
In the homo-classes, `Prototype` is a single function type such as `void ()`.
In the heter-classes, `PrototypeList` is a list of function types in `eventpp::HeterTuple`, such as `eventpp::HeterTuple<void (), void (std::string), void (int, int)>`.
Note: Ideally it would be better to use `std::tuple` instead of `eventpp::HeterTuple`, but the problem is that the tuple is instantiated in HeterEventDispatcher which cause compile error that function type can't be instantiated.
## Differences between heterogeneous classes vs homogeneous classes
1. Heterogeneous classes has both overhead on performance and memory usage. Usually event system is the core component in an application, the performance is critical.
2. Heterogeneous classes can't have the same API interface as homogeneous classes, because some APIs are impossible or very difficult to implement in heterogeneous classes.
3. Heterogeneous classes doesn't support eventpp::ArgumentPassingAutoDetect. That means the event in the argument can't be detected automatically.