自动推导基类模板的实际类型

问题描述 投票:0回答:2

我有许多类,每个类都定义了一个内部结构

Result
,并且都派生自
Base
类。我希望基类中有一个实现方法,该方法在派生类上调用时返回派生类 Result 结构。下面的代码可以工作,但是实际调用 get_result 时的类型重复让我很困扰。

如何让编译器自动将模板参数推导为实际的 get_result 调用?

额外限制:

当前锁定到 c++17,但如果有更新标准版本的解决方案,我也有兴趣看到它们。

#include <string>

using namespace std;

class Base
{
public:
    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;
}
c++ template-meta-programming
2个回答
1
投票

只需重写子类中的函数即可:

class Base
{
public:
    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;
    }

    Result get_result() {
        return Base::get_result<DerivedA>();
    }
};

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

    Result get_result() {
        return Base::get_result<DerivedB>();
    }
};

这样您就不会被迫发送用户代码中的类型:

{
    DerivedA a;
    const auto res = a.get_result();
}
{
    DerivedB b;
    const auto res = b.get_result();
}

另一种方法是使用 CRTP:

template<typename T>
class Base
{
public:
    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<DerivedA>
{
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<DerivedB>
{
public:
    struct Result
    {
        string s;
    };

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

在此示例中,如果您需要基于运行时的多态性,您可以添加另一个非模板基类。


0
投票

在 C++23 中,您可以使用显式对象根据调用该对象的静态类型来推导类型参数:

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

调用它就像常规模板一样进行推导:

const auto res = a.get_result();
© www.soinside.com 2019 - 2024. All rights reserved.