Hello,
while working on my Signal & Slot library, I faced a problem with variadic templates and references passed as parameter. Without reference all work very fine (with multiple arguments or without argument at all), but if any of the parameters is a reference, it does return an error at compile time to indicate that it was unable to find the matching constructor.
-- The following code is a very simplified example --
In the following example, DelayedSignal is the class I'm trying to fix and DelayedSignal_DebugHelper is used for debug/comparison purpose. They are almost identical, except the fact that DelayedSignal_DebugHelper use a class template for the third parameter of the last constructor, whereas the DelayedSignal use the method template for this same third argument.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
struct DelayedSignal
{
~DelayedSignal ()
{ std::cout << "~DelayedSignal CLOSE" << std::endl; }
template<class C, class... Args>
DelayedSignal ( void(C::*func)(Args...), C& obj )
{ std::cout << "DelayedSignal INIT - 03 - pointer to method & pointer to class instance (Arg num: " << sizeof...(Args) << ")" << std::endl; }
template<class C, class... Args>
DelayedSignal ( void(C::*func)(Args...), C& obj, Args... args )
{
std::cout << "DelayedSignal INIT - 04 - pointer to method & pointer to class instance & arguments (Arg num: " << sizeof...(Args) << ")" << std::endl;
}
};
template<class... ArgsBis>
struct DelayedSignal_DebugHelper
{
~DelayedSignal_DebugHelper ()
{ std::cout << "~DelayedSignal_DebugHelper CLOSE" << std::endl; }
template<class C, class... Args>
DelayedSignal_DebugHelper ( void(C::*func)(Args...), C& obj )
{ std::cout << "DelayedSignal_DebugHelper INIT - 03 - pointer to method & pointer to class instance (Arg num: " << sizeof...(Args) << ")" << std::endl; }
template<class C, class... Args>
DelayedSignal_DebugHelper ( void(C::*func)(Args...), C& obj, ArgsBis... args ) // Need to use ArgsBis instead of Args to make it work
{
std::cout << "DelayedSignal_DebugHelper INIT - 04 - pointer to method & pointer to class instance & arguments (Arg num: " << sizeof...(Args) << ")" << std::endl;
}
};
template <class Tr> struct Signal;
template <class Tr, class... Args>
struct Signal<Tr(Args...)>
{
void fire ( Args... args ) { std::cout << "Signal::fire::" << sizeof...(Args) << std::endl; }
};
struct Klass {};
int main()
{
std::string str1("Blop");
Klass k;
Signal<void (Klass&)> signal_01;
Signal<void (std::string&)> signal_02;
std::cout << "====== DelayedSignal :: needed for production purpose ===============" << std::endl;
// OK
DelayedSignal test01(&Signal<void (std::string&)>::fire, signal_02);
// HERE IS THE PROBLEM
//DelayedSignal test02(&Signal<void (std::string&)>::fire, signal_02, str1);
// OK
DelayedSignal test03(&Signal<void (Klass&)>::fire, signal_01);
// HERE IS THE PROBLEM
//DelayedSignal test04(&Signal<void (Klass&)>::fire, signal_01, k);
std::cout << "====== DelayedSignal_DebugHelper :: used only for debug purpose ======" << std::endl;
// OK
DelayedSignal_DebugHelper<std::string&> test05(&Signal<void (std::string&)>::fire, signal_02);
// OK
DelayedSignal_DebugHelper<std::string&> test06(&Signal<void (std::string&)>::fire, signal_02, str1);
// OK
DelayedSignal_DebugHelper<Klass&> test07(&Signal<void (Klass&)>::fire, signal_01);
// OK
DelayedSignal_DebugHelper<Klass&> test08(&Signal<void (Klass&)>::fire, signal_01, k);
return 1;
}
|
Error example:
1 2
|
main.cpp:2:74: error: no matching function for call to 'DelayedSignal::DelayedSignal(void (Signal<void(std::basic_string<char>&)>::*)(std::basic_string<char>&), Signal<void(std::basic_string<char>&)>&, std::string&)'
main.cpp:57:1: note: candidate is: DelayedSignal::DelayedSignal(const DelayedSignal&)
|
I was suggested on another forum that it could come from the fact that compiler deduce the same signature for a class instance and a reference to a class instance.
For instance, with test02 (line57), the compiler seems unable to know if "Args..." for the third argument is a std::string or a std::string& despite the fact that it does recognize "Args..." for the first argument.
I would like to be able to store all kinds of DelayedSignal into one std::list. That's why I would like to avoid the DelayedSignal_DebugHelper solution (Using this approach, would lead to use multiple std::list instead of one, and as I suppose that dispatching one list is more optimized than dispatching the undefined number of list required for all kinds of DelayedSignal, I'm trying to avoid this solution). I guess I could use pure virtual class as base class for DelayedSignal too if I want to store all of them into one std::list, but I'm trying to avoid virtual methods too.
Here there a solution to fix this problem ?
Thank you.
Edit : I compile this example using GCC 4.5.0 and the C++0x flag.