Search code examples
c++template-meta-programming

Automatically deduce actual type for base class template


I have a number of classes that each define an inner struct Result and are all derived from a Base class. I'd like to have a single implementation method in the base class that returns the derived classes Result struct when called on a derived class. The below code works, but the type duplication in the actual call to get_result bothers me.

How can I make the compiler automatically deduce the template argument to the actual get_result call?

Additional restrictions:

Currently locked to c++17, but if there is a solution with newer standard versions I'd be interested to see them as well.

Edit: Some clarifications:

Base is not just there for the get_result method, it has some properties itself and is also used for polymorphism, so it cannot be completely templated.

#include <string>

using namespace std;

class Base
{
public:
    // Some virtual stuff here that prevents the whole class from being templated. All derived classes need the s

    template<typename T>
    typename T::Result get_result() const
    {
        // static_assert(std::is_base_of_v<Base, T>, "Derived not derived from BaseClass");

        return T::Deserialize();
    }
};

class DerivedA
    : public Base
{
public:
    struct Result
    {
        int i = 0;
    };

    static Result Deserialize(/*In reality there is some stream argument here*/)
    {
        Result r;
        r.i = 42;
        return r;
    }
};

class DerivedB
    : public Base
{
public:
    struct Result
    {
        string s;
    };

    static Result Deserialize(/*In reality there is some stream argument here*/)
    {
        Result r;
        r.s = "Yatta";
        return r;
    }
};

int main()
{
    {
        DerivedA a;
        const auto res = a.get_result<DerivedA>();
    }
    {
        DerivedB b;
        const auto res = b.get_result<decltype(b)>();
    }
    return 0;
}

Edit 2:

For now just using a free function seems to be the best solution under the circumstances:

template <class T>
typename T::Result get_result(const T& instance)
{
    return T::Deserialize();
}

const auto res = get_result(a);

Solution

  • In C++23, you can use an explicit object to deduce the type parameter based on the static type of the object this is called on: (example)

    template<typename T>
    typename T::Result get_result(this const T&)
    //                            ^^^^
    {
        return T::Deserialize();
    }
    

    Calling it is like a regular template with deduction:

    const auto res = a.get_result();