我有以下情况:
template <typename T>
class Derived
{
public:
T m_Data;
};
我需要将它们存储起来,std::vector
所以我引入了一个抽象父类,如下所示:
class Base
{
public:
virtual unsigned int GetID() const = 0;
};
并Derived
继承自Base
:
template <typename T>
class Derived : public Base
{
public:
T m_Data;
unsigned int GetID() const override
{
return something;
}
}
所以现在我可以std::vector
像这样存储它们:
std::vector<Base*> m_Bases;
现在在程序执行的某个时刻,我需要迭代m_Bases
并根据某些条件选择一些并使用它们各自的m_Data
,如下所示:
void Foo()
{
for (auto& b : m_Bases)
{
if (some_condition)
{
// Access b->m_Data
}
}
}
由于我不能这样做,因为T m_Data
is member of Derived
and not Base
,所以我引入了一个访问者向量:
std::vector<std::function<void(Base*, std::function<void(std::any)>&)>> m_DerivedVisitors;
现在,当我插入一个新Derived<T>
的时,m_Bases
我也会插入一个新的 lambda,m_DerivedVisitors
这样我以后可以像这样调用:
template <typename T>
void CreateBase()
m_Bases.push_back(new Derived<T>());
m_DerivedVisitors.push_back([](Base* base, auto visitor)
{
if (dynamic_cast<Derived<T>*>(base) != nullptr)
{
visitor(static_cast<Derived<T>*>(base));
}
});
现在Foo
可能会Derived
在访问者通用 lambda(我希望)中获得一个实例,如下所示:
void Foo()
{
for (auto& b : m_Bases)
{
if (some_condition)
{
// Access b->m_Data
// Note that I made sure GetID() is returning counting index based on template instantiation
// So I could use it to index into m_Bases as-well as into m_DerivedVisitors
m_DerivedVisitors[b->GetID()](base, [&](auto derivedPtr) {
// Now I can access derivedPtr->m_Data and use outside variable with capture list
});
}
}
}
现在,如果您有敏锐的眼光,您会发现这段代码存在一些问题。
我不知道第二个参数m_DerivedVisitors
应该是什么类型。
现在它设置为,std::function<void(std::any)>&
因为我不知道访问者的类型,因为它的参数应该是Derived
模板类型,所以我尝试使用std::any
并且对于访问者本身,我使用了一个auto
参数,使其成为通用 lambda:
std::vector<std::function<void(Base*, std::function<void(std::any)>&)>> m_DerivedVisitors;
...
m_DerivedVisitors[b->GetID()](base, [&](auto derivedPtr) {
// Now I can access derivedPtr->m_Data and use outside variable with capture list
});
...
我不确定如何使它工作。
如果有人能用代码示例解释如何做到这一点,我将不胜感激。
提前致谢。
更新:
我想到了一个我根本不喜欢的解决方案,但无论如何我都会提出,也许它可以帮助某人帮助我改进它或找到另一个解决方案。我们改成m_DerivedVisitors
这样:
std::vector<std::function<void(Base*)>> m_DerivedVisitors;
我们像这样推动它:
m_DerivedVisitors.push_back([](Base* base)
{
if (dynamic_cast<Derived<T>*>(base) != nullptr)
{
Visit(static_cast<Derived<T>*>(base));
}
});
注意我们刮掉了auto visitor
函数参数,我们现在调用一个静态 Visit
方法,定义如下:
template<typename U>
static inline void Visit(U* object)
{
auto x = object->m_Data;
// Now we can do what ever we like with m_Data
}
我在这个解决方案中看到的问题是我无法具体说明我使用的访问者。解决此问题的一种方法是m_DerivedVisitors
再次更改为以下内容:
std::vector<std::function<void(Base*, unsigned int)>> m_DerivedVisitors;
并像这样改变我们推送它的方式:
m_DerivedVisitors.push_back([](Base* base, unsigned int id)
{
if (dynamic_cast<Derived<T>*>(base) != nullptr)
{
switch(id)
{
case 0:
Visit0(static_cast<Derived<T>*>(base));
break;
case 1:
Visit1(static_cast<Derived<T>*>(base));
break;
...
};
}
});
如您所见,我们必须将所有访问者定义为静态函数,并且我们必须使用id
. 我们甚至不能这样做,std::unordered_map
因为我们不能std::function
指向模板函数。
现在,每当有东西想将派生类型用于特定任务时,我们都必须定义一个静态函数并将其添加到 switch case 并使用适当的id
.
这不是唯一的问题。
最初在我的代码Foo
中给出了一个模板 lambda 参数以及我想传递m_Data
给它的方法,方法如下:
template <typename Fn>
void Foo(Fn&& fn)
{
for (auto& b : m_Bases)
{
if (some_condition)
{
// Access b->m_Data and pass it to fn
fn(b->m_Data); // Note this doesn't compile; just to demonstrate my intentions
}
}
}
现在,为了使用我提出的解决方案来做到这一点,我必须存储fn
一些静态/全局变量并在访问者函数中访问它,如下所示:
static int y = 8;
template<typename U>
static inline void Visit0(U* object)
{
// access y
auto x = object->m_Data;
}
template <typename Fn>
void Foo(Fn&& fn)
{
for (auto& b : m_Bases)
{
if (some_condition)
{
// Access b->m_Data and pass it to fn
y = 5;
m_DerivedVisitors[b->GetID()](b, 0);
}
}
}
请注意,这不适用于捕获模板类型,Fn&& fn
因为我不能拥有该类型的静态/全局变量,因为它完全是一个不同的范围。更不用说对于想要将其他变量引入访问函数的每种访问者,我都必须这样做,那是因为我不能像我最初的计划那样将 lambda 与捕获列表一起使用。
所以你可以看到这个解决方案根本不理想,它只是一个想法。
希望有人能想到更好的方法或指导我做其他事情。