Home > database >  derived class as a parameter of templated function which is specialized for its base class
derived class as a parameter of templated function which is specialized for its base class

Time:07-06

class Base {};

class Derived : public Base {};

class SomeClass
{
    template<typename T>
    static void SetContent(T* pChild, OVariant content)
    {
        LOG_ASSERT(0, "All classes must be specialized!.  Please provide implementation for this class.");
    }
};

template <>
void SomeClass::SetContent(Base* value) 
{
    LOG_TRACE("Yay!");
}

int main() {
    SomeClass foo;
    Derived derived;
    foo.SetContent(&derived);//want it to call SomeClass::SetContent(Base* value) 
    
    return 0;
}

When I call foo.SetContent(derived), I get the Assert. Is it not possible for the derived class to use the specialization for it's base class?

CodePudding user response:

You can convert a Derived* to a Base*, but I think you rather want to specialize for all T that have Base as base

#include <type_traits>
#include <iostream>
class Base {};

class Derived : public Base {};


template <typename T,typename = void>
struct impl {
    void operator()(T*) {
        std::cout <<"All classes must be specialized!.  Please provide implementation for this class.\n";
    }
};
template <typename T>
struct impl<T,std::enable_if_t<std::is_base_of_v<Base,T>>> {
    void operator()(T*) {
        std::cout << "Yay\n";
    }
};

class SomeClass
{
    public:
    template<typename T>
    static void SetContent(T* pChild)
    {
        impl<T>{}(pChild);    
    }
};

struct bar{};

int main() {
    SomeClass foo;
    Derived derived;
    foo.SetContent(&derived);
    bar b;
    foo.SetContent(&b);
}

Output:

Yay
All classes must be specialized!.  Please provide implementation for this class.

//want it to call SomeClass::SetContent(Base* value)

Note that if the template argument is deduced, it will be deduced as Derived not as Base and the argument is Derived*. SomeClass::SetContent<Base>(&derived); would already work as expected with your code (because Derived* can be converted to Base*).

CodePudding user response:

A workaround would be to have all SetContent's explicit specializations to form an overload set. You will have to do it yourself:

#include <iostream>
#include <utility>
#include <functional>

class Base {};

class Derived : public Base {};

template <class... T>
struct Overloads : T... {
    Overloads(const T &... t) : T(t)... {}
    using T::operator()...;
};

template <class R, class... Args>
struct FunctionP {
    using F = R(Args...);
    FunctionP(F *t) : t_(t) {}
    R operator()(Args... args) const {
        return std::invoke(t_, std::forward<Args>(args)...);
    }
    F *t_;
};

struct SomeClass {
    template<typename T>
    static void SetContent(T *x) {
        Overloads o(FunctionP(&SetContentImpl<Base>)); // enumerates all the specializations here
        if constexpr (std::is_invocable_v<decltype(o), T *>) {
            o(x);
        } else {
            SetContentImpl(x);
        }
    }

    template<typename T>
    static void SetContentImpl(T *) {
        std::cout << "1";
    }
};

template <>
void SomeClass::SetContentImpl(Base *) {
    std::cout << "2";
}

int main() {
    SomeClass foo;
    Derived derived;
    foo.SetContent(&derived);//want it to call SomeClass::SetContent(Base* value) 
    
    return 0;
}

godbolt

  • Related