【问题标题】:class inheriting from multiple template class instances returning their types从多个模板类实例继承的类返回它们的类型
【发布时间】:2021-09-02 04:32:52
【问题描述】:

我想从一个基模板类继承多次,并有一个方法为继承中使用的每种类型返回相应的值,看看test2实例(下面的代码肯定不会编译,因为@ 987654322@方法的签名相同,问题是我不知道如何修复它),我在代码中标记了重要的行:

#include <iostream>

namespace ns
{

template<typename Output>
class Base
{
protected:
    struct ResultData { bool success; Output data; };
public:
    virtual ResultData getNextData() = 0;
};

template<typename Output>
class Derived : public Base<Output> {/*implements some pure virtual method*/};

struct Data
{
    int32_t member;
};

struct Final final : public Derived<Data>
{
    ResultData getNextData() final
    {
        return { true, {1} };
    }
};

template<typename T1, typename T2 = int32_t>
    class DerivedMultiple : public virtual Derived<T1> // <= important
    , public Base<T2> // <= important
{
public:
    ResultData<T1> getNextData() override { return { false, {} }; }
    virtual ResultData<T2> getNextData() { return { false, {} }; }
};

struct FinalMultiple final : public DerivedMultiple<Data, std::string>
{
    ResultData getNextData() final { return {true, {1} }; } // <= important
    ResultData getNextData() final { return {true, "test"}; } // <= important
};

struct ReferenceT1 {
    ReferenceT1(Base<Data>& base) {}; // <= important
};

struct ReferenceT2 {
    ReferenceT2(Base<std::string>& base) {}; // <= important
};

}

int main()
{
    ns::Final test1;
    std::cout << test1.getNextData().success << std::endl;
    std::cout << test1.getNextData().data.member << std::endl;

    ns::FinalMultiple test2;
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData()/*for Data*/.data.member << std::endl; // <= important
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData()/*for std::string*/.data << std::endl; // <= important
    
    ns::ReferenceT1 referenceT1(test2); // <= important
    ns::ReferenceT2 referenceT2(test2); // <= important
}

有一个明显的解决方案是将值作为引用传递并将它们填充到getNextData(&lt;type&gt;&amp;) 方法中,但这就是它开始的方式,我在开始时重构了它,因为我想返回值,而不是填充它们(很多原因):

    bool getNextData(T1& output) override { return false; }
    bool getNextData(T2& output) override { return false; }

我尝试了虚拟继承,但在这种情况下没有帮助。

我想出了 2 个解决方案,它们都以一种非常肮脏的方式绕过了对不同签名的需求,并且当您取消注释其中的 2 条注释行时,它们都不起作用(不能从基类派生两次并拥有这些方法只返回预期的类型)。

第一个解决方案:

#include <iostream>

namespace ns
{

template<typename Output>
class Base
{
protected:
    struct ResultData { bool success; Output data; };
public:
    virtual ResultData getNextData() = 0;
};

template<typename Output>
class Derived : public Base<Output> {};

struct Data
{
    int32_t member;
};

struct Final final : public Derived<Data>
{
    ResultData getNextData() final
    {
        return { true, {1} };
    }
};

template<typename T1, typename T2 = int32_t>
    class DerivedMultiple : public virtual Derived<T1>
    //, public Base<T2>
{
protected:
    struct ResultDataT2 { bool success; T2 data; };
public:
    virtual ResultDataT2 getNextDataT2() { return { false, {} }; }
};

struct FinalMultiple final : public DerivedMultiple<Data, std::string>
{
    ResultData getNextData() final
    {
        return {true, {1} };
    }

    ResultDataT2 getNextDataT2() final
    {
        return {true, "test"};
    }
};

struct ReferenceT1 {
    ReferenceT1(Base<Data>& base) {};
};

struct ReferenceT2 {
    ReferenceT2(Base<std::string>& base) {};
};

}

int main()
{
    ns::Final test1;
    std::cout << test1.getNextData().success << std::endl;
    std::cout << test1.getNextData().data.member << std::endl;

    ns::FinalMultiple test2;
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData().data.member << std::endl;
    std::cout << test2.getNextDataT2().success << std::endl;
    std::cout << test2.getNextDataT2().data << std::endl;
    
    ns::ReferenceT1 referenceT1(test2);
    //ns::ReferenceT2 referenceT2(test2);
}

第二种解决方案:

#include <iostream>

namespace ns
{

template<typename Output>
struct ResultData { bool success; Output data; };

template<typename Output>
class Base
{
public:
    virtual ResultData<Output> getNextData() = 0;
};

template<typename Output>
class Derived : public Base<Output> {};

struct Data
{
    int32_t member;
};

struct Final final : public Derived<Data>
{
    ResultData<Data> getNextData() final
    {
        return { true, {1} };
    }
};

template<typename T1, typename T2 = int32_t>
    class DerivedMultiple : public virtual Derived<T1>
    //, public Base<T2>
{
protected:
    struct ResultDataT2 { bool success; T2 data; };
public:
    virtual ResultDataT2 getNextDataT2() { return { false, {} }; }
};

struct FinalMultiple final : public DerivedMultiple<Data, std::string>
{
    ResultData<Data> getNextData() final
    {
        return {true, {1} };
    }

    ResultDataT2 getNextDataT2() final
    {
        return {true, "test"};
    }
};

struct ReferenceT1 {
    ReferenceT1(Base<Data>& base) {};
};

struct ReferenceT2 {
    ReferenceT2(Base<std::string>& base) {};
};

}

int main()
{
    ns::Final test1;
    std::cout << test1.getNextData().success << std::endl;
    std::cout << test1.getNextData().data.member << std::endl;

    ns::FinalMultiple test2;
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData().data.member << std::endl;
    std::cout << test2.getNextDataT2().success << std::endl;
    std::cout << test2.getNextDataT2().data << std::endl;
    
    ns::ReferenceT1 referenceT1(test2);
    //ns::ReferenceT2 referenceT2(test2);
}

在 C++ 中有没有办法克服这个问题?

另一件事是在解决方案中切换到可变参数模板(这样基类可以被继承两次以上,而乘派派生类可以返回两种以上的类型),但这是可选的。

【问题讨论】:

    标签: c++ templates inheritance c++17 multiple-inheritance


    【解决方案1】:

    我不确定我是否完全理解你想要达到的目标,但看看这是否会给你一些想法。

    template <typename Impl, typename Intf, typename Tag>
    class ForwardingShim;
    
    template <typename Impl, typename Intf>
    class ForwardingShim<Impl, Intf, struct ForwardOne> : public Intf {
    public:
      typename Intf::ResultData getNextData() final {
        return static_cast<Impl*>(this)->getNextDataOne();
      }
    };
    
    template <typename Impl, typename Intf>
    class ForwardingShim<Impl, Intf, struct ForwardTwo> : public Intf {
    public:
      typename Intf::ResultData getNextData() final {
        return static_cast<Impl*>(this)->getNextDataTwo();
      }
    };
    
    template <typename T1, typename T2>
    class MultiDerived :
        public ForwardingShim<MultiDerived, Base<T1>, struct ForwardOne>,
        public ForwardingShim<MultiDerived, Base<T2>, struct ForwardTwo> {
    public:
      typename Base<T1>::ResultData getNextDataOne();
      typename Base<T2>::ResultData getNextDataTwo();
    };
    

    Demo

    【讨论】:

    • 谢谢,我让它工作并为我的目的做了一些改进,将发布有关它的更新,但现在没有时间。
    • 所以我认为这个答案仍然是唯一的答案,它已经足够好了,但这不是一个最小的例子。我懒得用我的回答来改进这个问题,例如删除static_casts 和typenames 等,使用最低限度来简化它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 2022-10-21
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    相关资源
    最近更新 更多