传递对Variadic模板的引用

时间:2011-04-04 04:32:55

标签: c++ templates c++11 variadic

我正在研究一个事件库,我遇到了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相同,只是它在最后一个构造函数而不是Args模板(方法模板参数)上使用ArgsBis(类模板参数),否则它不起作用(与DelayedSignal一样)。 void(C::*func)(Args...)接受了Args,但ArgsBis... args没有使用它们,因为它们在同一个构造函数声明中。

据我所知,没有引用(例如DelayedSignal test04(&Signal<void, Klass>::fire, signal_01, k);)或多个参数(或没有参数)没有问题,只要没有引用。

有没有解决这个问题?

谢谢。

3 个答案:

答案 0 :(得分: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的冲突类型:

  1. std::string&
  2. std::string
  3. 我认为解决此问题的最佳方式正是您使用DelayedSignal_DebugHelper完成此操作的方式。

答案 1 :(得分:1)

Howard Hinnant是对的...你有另一种可能性就是在任何地方使用引用,例如:

#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 :(得分:1)

你已经接受了答案,这只是补充。 虽然我可能会忽略某些内容,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 )
    {
       ...
    }
};

这是test on ideone