我试图用元编程来防止重复代码父子结构。 我得到它的工作达到一定点。
在底部的编译器和欠幅正确地显示的代码,但有些关系( /*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);
}
......这不应该是可能的,因为Human
和Parasite
并没有直接关系。
所以,我该如何正确保持this
顶部(最衍生物)类的类型限定,符合以Human
, Dog
等类型?
工作代码(带注释):
#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 ++ 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);
}
};
演示
用C ++ 14,那将是更详细取代“for_each_tuple”与完成std::apply
和折叠的表达。
和在C ++ 11,甚至更用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;
这似乎解决我的问题,因为我现在可以使用
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();
}
活生生的例子