我有以下情况:
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_Datais member of Derivedand 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 与捕获列表一起使用。
所以你可以看到这个解决方案根本不理想,它只是一个想法。
希望有人能想到更好的方法或指导我做其他事情。