获取此对象的原始类型

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

我正在尝试使用元编程来防止父子结构中的重复代码。我把它弄到了一定程度。

底部编译器显示的代码正确,并且一些关系(/*Tree_tag,*//*Parasite_tag*/)被注释掉了。如果取消注释,MSVS2017会显示

error C2664: 'void Obj<std::tuple<Human_tag>,std::tuple<>>::removeParent(const Obj<std::tuple<>,std::tuple<Tree_tag,Dog_tag>> *const )': cannot convert argument 1 from 'Obj<std::tuple<>,std::tuple<Dog_tag>> *' to 'const Obj<std::tuple<>,std::tuple<Tree_tag,Dog_tag>> *const '
note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

和G ++显示

In instantiation of ‘void Obj<std::tuple<>, std::tuple<_El0, _El ...> ::removeAllChildren() [with TChildTag = Dog_tag; TChildTags = {}]’:
Main.cpp:126:1:   required from here
Main.cpp:73:43: error: invalid conversion from ‘Obj<std::tuple<>, std::tuple<Dog_tag> >*’ to ‘const TParent* {aka const Obj<std::tuple<>, std::tuple<Tree_tag, Dog_tag> >*}’ [-fpermissive]
    for (auto&& child : childrenPtrs) child->removeParent(this);

问题在于this类型限定符。因为我迭代地剥离了模板参数

class Obj<std::tuple<>, std::tuple<TChildTag, TChildTags...>>
: public Obj<std::tuple<>, std::tuple<TChildTags...>>

生成的基本类型的this与原始类型不匹配。像错误显示:Human = Obj<std::tuple<>,std::tuple<Tree_tag,Dog_tag>>的原始类型。然而,由于迭代剥离,基数中的this类型是Obj<std::tuple<>,std::tuple<Dog_tag>>

我试图按照建议使用reinterpret_cast

template<typename T>
void addParent(T* const parentPtr) {
    parentsPtrs.push_back(reinterpret_cast<TParent* const>(parentPtr));
}
template<typename T>
void removeParent(T const* const parentPtr) {
    auto it = std::find(std::cbegin(parentsPtrs), std::cend(parentsPtrs),
        reinterpret_cast<TParent const* const>(parentPtr));
    if (it != std::cend(parentsPtrs)) parentsPtrs.erase(it);
}

但问题是所有内容都被转换为允许的参数。即这段代码有效:

int main() {
    Human h1;
    Parasite p1;
    addRelation(&h1, &p1);
}

......这是不可能的,因为HumanParasite没有直接关系。

那么我怎样才能正确地保持顶级(最衍生)类的qazxsw poi类型限定符,符合qazxsw poi,qazxsw poi等类型?

工作代码(带注释):

this
c++ parent-child metaprogramming variadic-templates
2个回答
1
投票

使用C ++ 17,您可以(没有强制转换):

Human

Dog

使用C ++ 14,用#include <tuple> #include <vector> template<class T> using prtVector = std::vector<T*>; class BaseObject { public: virtual prtVector<BaseObject> getAllParents() const = 0; virtual prtVector<BaseObject> getAllChildren() const = 0; virtual void removeAllParents() = 0; virtual void removeAllChildren() = 0; }; template<typename TParentTuple, typename TChilderenTuple> class Obj; template<typename TParentTag, typename... TParentTags, typename... TChildTags> class Obj<std::tuple<TParentTag, TParentTags...>, std::tuple<TChildTags...>> : public Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>> { using TParent = typename TParentTag::obj_type; prtVector<TParent> parentsPtrs; public: void addParent(TParent* const parentPtr) { parentsPtrs.push_back(parentPtr); } void removeParent(TParent const* const parentPtr) { auto it = std::find(std::cbegin(parentsPtrs), std::cend(parentsPtrs), parentPtr); if (it != std::cend(parentsPtrs)) parentsPtrs.erase(it); } virtual prtVector<BaseObject> getAllParents() const override { auto result = Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllParents(); result.insert(std::begin(result), std::cbegin(parentsPtrs), std::cend(parentsPtrs)); return result; } virtual prtVector<BaseObject> getAllChildren() const override { return Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllChildren(); } virtual void removeAllParents() override { Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllParents(); for (auto&& parent : parentsPtrs) parent->removeChild(this); } virtual void removeAllChildren() override { Obj<std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllChildren(); } }; template<typename TChildTag, typename... TChildTags> class Obj<std::tuple<>, std::tuple<TChildTag, TChildTags...>> : public Obj<std::tuple<>, std::tuple<TChildTags...>> { using TChild = typename TChildTag::obj_type; prtVector<TChild> childrenPtrs; public: void addChild(TChild* const childPtr) { childrenPtrs.push_back(childPtr); } void removeChild(TChild const* const childPtr) { auto it = std::find(std::cbegin(childrenPtrs), std::cend(childrenPtrs), childPtr); if (it != std::cend(childrenPtrs)) childrenPtrs.erase(it); } virtual prtVector<BaseObject> getAllParents() const override { return Obj<std::tuple<>, std::tuple<TChildTags...>>::getAllChildren(); } virtual prtVector<BaseObject> getAllChildren() const override { auto result = Obj<std::tuple<>, std::tuple<TChildTags...>>::getAllChildren(); result.insert(std::begin(result), std::cbegin(childrenPtrs), std::cend(childrenPtrs)); return result; } virtual void removeAllParents() override {} virtual void removeAllChildren() override { Obj<std::tuple<>, std::tuple<TChildTags...>>::removeAllChildren(); for (auto&& child : childrenPtrs) child->removeParent(this); } }; template<> class Obj<std::tuple<>, std::tuple<>> : public BaseObject { public: virtual prtVector<BaseObject> getAllParents() const override { return prtVector<BaseObject>(); } virtual prtVector<BaseObject> getAllChildren() const override { return prtVector<BaseObject>(); } virtual void removeAllParents() override {} virtual void removeAllChildren() override {} }; struct Human_tag; struct Tree_tag; struct Dog_tag; struct Parasite_tag; using Human = Obj<std::tuple<>, std::tuple</*Tree_tag,*/ Dog_tag>>; using Tree = Obj<std::tuple<Human_tag>, std::tuple<>>; using Dog = Obj<std::tuple<Human_tag>, std::tuple</*Parasite_tag*/>>; using Parasite = Obj<std::tuple<Dog_tag>, std::tuple<>>; struct Human_tag { using obj_type = Human; }; struct Tree_tag { using obj_type = Tree; }; struct Dog_tag { using obj_type = Dog; }; struct Parasite_tag { using obj_type = Parasite; }; template<class A, class B> void addRelation(A* a, B* b) { a->addChild(b); b->addParent(a); } #include <iostream> int main() { Human h1; Dog d1, d2; addRelation(&h1, &d1); addRelation(&h1, &d2); auto result = h1.getAllChildren(); std::cout << result.size() << "\n"; //print 2 d1.removeAllParents(); result = h1.getAllChildren(); std::cout << result.size() << "\n"; //print 1 std::cin.ignore(); } 和折叠表达式替换“for_each_tuple”会更加冗长。

在C ++ 11中,使用template<typename TParentTuple, typename TChilderenTuple> class Obj; template<typename... ParentTags, typename... ChildTags> class Obj<std::tuple<ParentTags...>, std::tuple<ChildTags...>> : public BaseObject { std::tuple<std::vector<typename ParentTags::obj_type*>...> parents; std::tuple<std::vector<typename ChildTags::obj_type*>...> children; public: template <typename T> void addParent(T* parent) { std::get<std::vector<T*>>(parents).push_back(parent); } template <typename T> void removeParent(const T* parent) { auto& v = std::get<std::vector<T*>>(parents); auto it = std::find(std::cbegin(v), std::cend(v), parent); if (it != std::cend(v)) { v.erase(it); } } template <typename T> void addChild(T* child) { std::get<std::vector<T*>>(children).push_back(child); } template <typename T> void removeChild(const T* child) { auto& v = std::get<std::vector<T*>>(children); auto it = std::find(std::cbegin(v), std::cend(v), child); if (it != std::cend(v)) { v.erase(it); } } std::vector<BaseObject*> getAllParents() const override { std::vector<BaseObject*> res; std::apply([&](auto&... v){ (res.insert(res.end(), v.begin(), v.end()), ...); }, parents); return res; } std::vector<BaseObject*> getAllChildren() const override { std::vector<BaseObject*> res; std::apply([&](auto&... v){ (res.insert(res.end(), v.begin(), v.end()), ...); }, children); return res; } void removeAllParents() override { std::apply( [this](auto&... v) { [[maybe_unused]] auto clean = [this](auto& v) { for (auto* parent : v) { parent->removeChild(this); } v.clear(); }; (clean(v), ...); }, parents); } void removeAllChildren() override { std::apply( [this](auto&... v) { [[maybe_unused]] auto clean = [this](auto& v) { for (auto* child : v) { child->removeParent(this); } v.clear(); }; ( clean(v), ...); }, children); } }; 甚至更多。


0
投票

好的,我已经考虑了很多,并决定将原始类型添​​加为另一个模板参数。所以类签名将是这样的:

Demo

这似乎解决了我的问题,因为我现在可以使用

std::apply

然后完整的工作代码变为:

std::get<T>(tuple)

template<typename TOwnTag, typename TParentTag, typename... TParentTags, typename... TChildTags> class Obj<TOwnTag, std::tuple<TParentTag, TParentTags...>, std::tuple<TChildTags...>> : public Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>> { using TOwn = typename TOwnTag::obj_type;

© www.soinside.com 2019 - 2024. All rights reserved.