【问题标题】:getting original type of this object获取此对象的原始类型
【发布时间】:2018-07-04 12:07:08
【问题描述】:

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

底部编译器和runt 中显示的代码正确,但一些关系(/*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&lt;std::tuple&lt;&gt;,std::tuple&lt;Tree_tag,Dog_tag&gt;&gt;。但是由于迭代剥离,基中this的类型是Obj&lt;std::tuple&lt;&gt;,std::tuple&lt;Dog_tag&gt;&gt;

我尝试按照建议使用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);
}

但问题是 everything 被强制转换为允许的参数。 IE。此代码有效:

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

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

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

工作代码(使用 cmets):

#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();
}

【问题讨论】:

    标签: c++ parent-child metaprogramming variadic-templates


    【解决方案1】:

    使用 C++17,您可以这样做(无需强制转换):

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

    Demo

    在 C++14 中,用 std::apply 和折叠表达式替换“for_each_tuple”会更冗长。

    在 C++11 中,std::get&lt;T&gt;(tuple) 更是如此。

    【讨论】:

    • 哇,太好了,这样就不需要强制转换了。谢谢你。 I was trying this before,但由于我不熟悉 std::get&lt;std::vector&lt;T*&gt;&gt;(parents)std::apply 之类的东西(并且没有找到任何建议使用它们的帖子),我无法让它工作。因此我的解决方案如下。
    • 顺便说一句,我认为你需要OwnTag,否则你可能对using Tree = Obj&lt;std::tuple&lt;Human_tag&gt;, std::tuple&lt;&gt;&gt;; using House = Obj&lt;std::tuple&lt;Human_tag&gt;, std::tuple&lt;&gt;&gt;; 有问题(HouseTree 将是同一个对象)。
    【解决方案2】:

    好的,我对此考虑了很多,并决定将原始类型添​​加为另一个模板参数。 所以类签名将类似于:

    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;
    

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

    reinterpret_cast<TOwn* const>(this)
    

    那么完整的工作代码就变成了:

    #include <tuple>
    #include <vector>
    #include <algorithm>
    
    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 TOwnTag, typename TParentTuple, typename TChilderenTuple>
    class Obj;
    
    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;
        using TParent = typename TParentTag::obj_type;
        prtVector<TParent> parentsPtrs;
    public:
        //prevent base function hiding
        using Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::addParent;
        using Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeParent;
    
        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<TOwnTag, 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<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::getAllChildren();
        }
        virtual void removeAllParents() override {
            Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllParents();
            for (auto&& parent : parentsPtrs) parent->removeChild(reinterpret_cast<TOwn* const>(this));
        }
        virtual void removeAllChildren() override {
            Obj<TOwnTag, std::tuple<TParentTags...>, std::tuple<TChildTags...>>::removeAllChildren();
        }
    };
    
    template<typename TOwnTag, typename TChildTag, typename... TChildTags>
    class Obj<TOwnTag, std::tuple<>, std::tuple<TChildTag, TChildTags...>>
        : public Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>
    {
        using TOwn = typename TOwnTag::obj_type;
        using TChild = typename TChildTag::obj_type;
        prtVector<TChild> childrenPtrs;
    public:
        void addParent() {}
        void removeParent() {}
        //prevent base function hiding
        using Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::addChild;
        using Obj<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::removeChild;
    
        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<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::getAllParents();
        }
        virtual prtVector<BaseObject> getAllChildren() const override {
            auto result = Obj<TOwnTag, 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<TOwnTag, std::tuple<>, std::tuple<TChildTags...>>::removeAllChildren();
            for (auto&& child : childrenPtrs) child->removeParent(reinterpret_cast<TOwn* const>(this));
        }
    };
    
    template<typename TOwnTag>
    class Obj<TOwnTag, std::tuple<>, std::tuple<>> : public BaseObject {
    public:
        void addChild() {}
        void removeChild() {}
        void addParent() {}
        void removeParent() {}
        //
        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<Human_tag, std::tuple<>, std::tuple<Tree_tag, Dog_tag>>;
    using Tree = Obj<Tree_tag, std::tuple<Human_tag>, std::tuple<>>;
    using Dog = Obj<Dog_tag, std::tuple<Human_tag>, std::tuple<Parasite_tag>>;
    using Parasite = Obj<Parasite_tag, 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* const a, B* const b)
    {
        a->addChild(b);
        b->addParent(a);
    }
    
    #include <iostream>
    int main() {
        Human h1;
        Dog d1, d2;
        //Parasite p1;
    
        addRelation(&h1, &d1);
        addRelation(&h1, &d2);
        //addRelation(&h1, &p1); // compiler error
        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();
    }
    

    Live Example

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-12
      • 2016-03-30
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多