Передача ссылок на шаблоны Variadic

Я работаю над библиотекой Event и сталкиваюсь с проблемой с шаблонами Variadic.

Все работает очень хорошо, за исключением того, что я не могу передавать ссылки в качестве параметров...

Вот очень упрощенный пример, написанный, чтобы разоблачить мою проблему.

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, class... Args >
struct Signal
{
    void fire ( Args... args ) { std::cout << "Signal::fire::" << sizeof...(Args) << std::endl; }
};

struct Klass {};


int main()
{
    std::string str1("Blop");   // Will be used as reference
    Klass k;                    // Will be used as reference

    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;
}

Когда я регистрирую все экземпляры DelayedSignal в одном экземпляре std:: list, я бы хотел избежать использования шаблона в самом классе и тем самым вместо этого использовать шаблоны для конструкторов. Я мог бы также использовать чистый виртуальный класс в качестве базы для всех DelayedSignal и указать указатели на виртуальный класс в std:: list, но я думаю, что лучше всего минимизировать использование виртуальных методов, и я действительно заинтригован этой проблемой...

Как вы можете видеть в этом примере, test02 и test04 возвращают ошибки, если они активированы. DelayedSignal_DebugHelper почти идентичен DelayedSignal, за исключением того факта, что он использует ArgsBis (аргумент шаблона класса) для последнего конструктора вместо шаблона Args (аргумент шаблона метода), иначе он не работает (как в случае с DelayedSignal). Аргус принимается в void(C::*func)(Args...), но не с ArgsBis... args, то есть тот факт, что они находятся в одном объявлении конструктора.

Насколько я знаю, нет проблем без ссылок (например, DelayedSignal test04(&Signal<void, Klass>::fire, signal_01, k);) или с несколькими параметрами (или none), если нет ссылок.

Есть ли способ решить эту проблему?

Спасибо.

Ответ 1

Говард Хиннант прав... Другая возможность, которую вы имеете, - использовать ссылки повсюду, например:

#include <iostream>

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 Tr, class... Args >
struct Signal
{
     void fire ( Args &... args ) { std::cout << "Signal::fire::" << sizeof...(Args) << std::endl; }
};

struct Klass {};

int main()
{
    std::string str1("Blop");   // Will be used as reference
    Klass k;                    // Will be used as reference

    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);

}

Ответ 2

Я использую clang, который дает абсолютно невероятное сообщение об ошибке:

test.cpp:59:19: error: no matching constructor for initialization of 'DelayedSignal'
    DelayedSignal test02(&Signal<void, std::string&>::fire, signal_02, str1);
                  ^      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:9:5: note: candidate constructor template not viable: requires 2 arguments, but 3 were provided
    DelayedSignal ( void(C::*func)(Args...), C& obj )
    ^
test.cpp:13:5: note: candidate template ignored: deduced conflicting types for parameter 'Args'
      (<std::__1::basic_string<char> &> vs. <std::__1::basic_string<char>>)
    DelayedSignal ( void(C::*func)(Args...), C& obj, Args... args )
    ^

Компилятор выводит конфликтующие типы для Args:

  • std::string&
  • std::string

Я считаю, что лучший способ исправить это именно так, как вы это сделали с помощью DelayedSignal_DebugHelper.

Ответ 3

Как вы приняли ответ, это всего лишь дополнение. Хотя я могу пропустить что-то, identity шаблон шаблона, например, следующий, похоже, сделает ваш код компилируемым.
Например:

template<class T> struct identity { typedef T type; };

struct DelayedSignal
{
    ...
    template<class C, class... Args>
    DelayedSignal ( void(C::*func)(Args...), C& obj, typename identity<Args>::type... args )
    {
       ...
    }
};

Вот тест на ideone