简体   繁体   中英

Termination issue on recursive template function in variadic template

I am trying to create a utility class that will call a specific function on all classes in a list. The purpose behind this is to automate an element of reflection within a hierarchy of classes.

I'm using Visual Studio 2015 to compile some C++ code and I'm getting a compile error when the recursive template function is unfolded because the compiler is having trouble distinguishing between the recursive function and the terminating function.

I've extracted out the core of the class to a simple test case:

#include <iostream>

template< typename ... BaseClasses >
class Meta
{
public:
    virtual ~Meta() {}

    template< typename T >
    void call(const T& val)
    {
        callOnAllClasses<T, BaseClasses...>(val);
    }

private:
    template< typename T, typename HeadClass >
    void callOnAllClasses(const T& val)
    {
        auto pObj = dynamic_cast<HeadClass*>(this);
        if ( pObj )
            pObj->HeadClass::doSomething(val);
    }

    template< typename T, typename HeadClass, typename ... TailClasses >
    void callOnAllClasses(const T& val)
    {
        auto pObj = dynamic_cast<HeadClass*>(this);
        if ( pObj )
            pObj->HeadClass::doSomething(val);
        callOnAllClasses<T, TailClasses...>(val);
    }
};

class A
{
public:
    void doSomething(int i)
    {
        std::cout << "A:" << i << std::endl;
    }
};

class B
{
public:
    void doSomething(int i)
    {
        std::cout << "B:" << i << std::endl;
    }
};

class C : public B, public A, public Meta<C,B,A>
{
    public:
    void doSomething(int i)
    {
        std::cout << "C:" << i << std::endl;
    }
};

int main()
{
    C c;
    c.call(5);
}

This results in the following error when compiled in Visual Studio 2015:

error C2668: 'Meta<C,B,A>::callOnAllClasses': ambiguous call to overloaded function
could be 'void Meta<C,B,A>::callOnAllClasses<T,A,>(const T &)'
or       'void Meta<C,B,A>::callOnAllClasses<T,A>(const T &)'

I've never used variadic templates before so I'm at a bit of a loss as to why this might be going wrong. Any help would be much appreciated!

You problem can be minimized as follows:

template< typename ... Bases >
struct Meta
{
    template< typename T >
    void call(const T& val)
    {
        callOnAllClasses<T, Bases...>(val);
    }

    template< typename T, typename HeadClass >
    void callOnAllClasses(const T& val)
    {

    }

    template< typename T, typename HeadClass, typename ... TailClasses >
    void callOnAllClasses(const T& val)
    {
        callOnAllClasses<T, TailClasses...>(val);
    }
};

struct C : Meta<int, int, int> { };

int main()
{
    C{}.call(5);
}

When TailClasses is empty, both these signatures are ambiguous to the compiler:

template< typename T, typename HeadClass >
void callOnAllClasses(const T& val);

template< typename T, typename HeadClass, typename ... TailClasses >
void callOnAllClasses(const T& val);

Adding an extra template argument to the recursive case helps the compiler disambiguate between the variadic and non-variadic overloads when TailClasses is empty.

template< typename T, typename HeadClass, typename T1, typename ... TailClasses >
void callOnAllClasses(const T& val)
{
    auto pObj = dynamic_cast<HeadClass*>(this);
    if ( pObj )
        pObj->HeadClass::doSomething(val);
    callOnAllClasses<T, T1, TailClasses...>(val);
}

live example on godbolt

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM