【发布时间】: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(<type>&) 方法中,但这就是它开始的方式,我在开始时重构了它,因为我想返回值,而不是填充它们(很多原因):
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