在编译时获取类型 T 的列表条目
Get list entry of type T at compile-time
我有一个容器 class,它存储一些抽象对象 class。在某些部分,程序需要在实现 classes 时获取这些对象。我写了一个通用的 get 函数,但它必须循环遍历所有存储的对象,这会花费很多时间。据我所知,容器将只有一种类型的一个对象,我想在编译时解决这个问题,但不想为每种类型手动创建我自己的成员。
目前 "wasted" 运行时我没有遇到问题,但我想了解如何解决这个问题。
我当前的实现:
#include <iostream>
#include <list>
class Abstract
{
public:
virtual void update() = 0;
};
class ImplA : public Abstract
{
public:
void update() {std::cout << "implA" << std::endl;}
};
class ImplB : public Abstract
{
public:
void update() {std::cout << "implB" << std::endl;}
};
class Container
{
public:
Container(){
content.push_back(new ImplA);
content.push_back(new ImplB);
}
void update() {
for (Abstract* obj : content)
obj->update();
}
template<typename T> T* get() const {
for (Abstract* obj : content) {
if(dynamic_cast<T*>(obj) != nullptr)
return dynamic_cast<T*>(obj);
}
return nullptr;
}
private:
std::list<Abstract*> content;
};
int main()
{
Container* container = new Container();
container->get<ImplA>()->update();
container->get<ImplB>()->update();
return 0;
}
我对如何解决这个问题的想法:
- 创建自己的成员并为每种类型获取函数
- 不使用列表而是使用映射并使用 typeinfo.name 作为键(但这是一个运行时解决方案,不是一个好主意)
- 实现类型列表(例如枚举类型 {implA, implB})并将其用作映射键。在这里,我必须为每种类型创建一个新条目。
- 使用编译时映射之类的东西,它可以使用类型作为键。
提前致谢!
至少有两种主要方法可以提供对不同类型对象的恒定时间访问:
- 使用多重继承,向下转换为对象类型。
- 使用由
std::type_index
. 索引的散列 table(如 std::unordered_set
)
提供编译时访问的多重继承方法示例:
#include <iostream>
#include <vector>
using namespace std;
class Abstract
{
public:
virtual void update() = 0;
};
class Impl_a : public Abstract
{
public:
void update() override { cout << "impl A" << endl; }
};
class Impl_b : public Abstract
{
public:
void update() override { cout << "impl B" << endl; }
};
template< class Type >
struct Boxed_
{
Type object;
};
class Container:
public Boxed_<Impl_a>,
public Boxed_<Impl_b>
{
vector<Abstract*> m_items;
Container( const Container& ) = delete;
auto operator=( const Container& ) -> Container& = delete;
public:
void update() // Not an override of base class function
{
for( Abstract* obj : m_items ) { obj->update(); }
}
template< class Type >
auto get()
-> Type*
{ return &static_cast<Boxed_<Type>&>( *this ).object; }
Container():
m_items{{ get<Impl_a>(), get<Impl_b>() }}
{}
};
auto main()
-> int
{
Container c;
#ifdef INDIVIDUAL_CALLS
c.get<Impl_a>()->update();
c.get<Impl_b>()->update();
#else
c.update();
#endif
}
如果您希望容器作为 const
非 const
对象引用的持有者,只需添加一个额外的间接级别。
这是软件工程的基本定理,作者 David Wheeler:
” All problems in computer science can be solved by another level of indirection
如果你的对象总是插入在一起,那么你的问题已经被std::tuple
解决了:
#include <iostream>
#include <tuple>
template <class... Ts>
struct Container : std::tuple<Ts...> {
using std::tuple<Ts...>::tuple;
void update() {
std::apply([](auto &... objects){ (objects.update(), ...); }, *this);
}
};
namespace std {
template <class... Ts>
struct tuple_size<Container<Ts...>> : tuple_size<tuple<Ts...>> { };
}
template <class... Ts>
Container(Ts...) -> Container<Ts...>;
Container
通过隐式转换为 std::tuple
和 std::tuple_size
.
的特化来支持 std::get
See it Live on Coliru(针对 C++17 之前的 GCC 进行了调整:/)
我有一个容器 class,它存储一些抽象对象 class。在某些部分,程序需要在实现 classes 时获取这些对象。我写了一个通用的 get 函数,但它必须循环遍历所有存储的对象,这会花费很多时间。据我所知,容器将只有一种类型的一个对象,我想在编译时解决这个问题,但不想为每种类型手动创建我自己的成员。
目前 "wasted" 运行时我没有遇到问题,但我想了解如何解决这个问题。
我当前的实现:
#include <iostream>
#include <list>
class Abstract
{
public:
virtual void update() = 0;
};
class ImplA : public Abstract
{
public:
void update() {std::cout << "implA" << std::endl;}
};
class ImplB : public Abstract
{
public:
void update() {std::cout << "implB" << std::endl;}
};
class Container
{
public:
Container(){
content.push_back(new ImplA);
content.push_back(new ImplB);
}
void update() {
for (Abstract* obj : content)
obj->update();
}
template<typename T> T* get() const {
for (Abstract* obj : content) {
if(dynamic_cast<T*>(obj) != nullptr)
return dynamic_cast<T*>(obj);
}
return nullptr;
}
private:
std::list<Abstract*> content;
};
int main()
{
Container* container = new Container();
container->get<ImplA>()->update();
container->get<ImplB>()->update();
return 0;
}
我对如何解决这个问题的想法:
- 创建自己的成员并为每种类型获取函数
- 不使用列表而是使用映射并使用 typeinfo.name 作为键(但这是一个运行时解决方案,不是一个好主意)
- 实现类型列表(例如枚举类型 {implA, implB})并将其用作映射键。在这里,我必须为每种类型创建一个新条目。
- 使用编译时映射之类的东西,它可以使用类型作为键。
提前致谢!
至少有两种主要方法可以提供对不同类型对象的恒定时间访问:
- 使用多重继承,向下转换为对象类型。
- 使用由
std::type_index
. 索引的散列 table(如
std::unordered_set
)
提供编译时访问的多重继承方法示例:
#include <iostream>
#include <vector>
using namespace std;
class Abstract
{
public:
virtual void update() = 0;
};
class Impl_a : public Abstract
{
public:
void update() override { cout << "impl A" << endl; }
};
class Impl_b : public Abstract
{
public:
void update() override { cout << "impl B" << endl; }
};
template< class Type >
struct Boxed_
{
Type object;
};
class Container:
public Boxed_<Impl_a>,
public Boxed_<Impl_b>
{
vector<Abstract*> m_items;
Container( const Container& ) = delete;
auto operator=( const Container& ) -> Container& = delete;
public:
void update() // Not an override of base class function
{
for( Abstract* obj : m_items ) { obj->update(); }
}
template< class Type >
auto get()
-> Type*
{ return &static_cast<Boxed_<Type>&>( *this ).object; }
Container():
m_items{{ get<Impl_a>(), get<Impl_b>() }}
{}
};
auto main()
-> int
{
Container c;
#ifdef INDIVIDUAL_CALLS
c.get<Impl_a>()->update();
c.get<Impl_b>()->update();
#else
c.update();
#endif
}
如果您希望容器作为 const
非 const
对象引用的持有者,只需添加一个额外的间接级别。
这是软件工程的基本定理,作者 David Wheeler:
” All problems in computer science can be solved by another level of indirection
如果你的对象总是插入在一起,那么你的问题已经被std::tuple
解决了:
#include <iostream>
#include <tuple>
template <class... Ts>
struct Container : std::tuple<Ts...> {
using std::tuple<Ts...>::tuple;
void update() {
std::apply([](auto &... objects){ (objects.update(), ...); }, *this);
}
};
namespace std {
template <class... Ts>
struct tuple_size<Container<Ts...>> : tuple_size<tuple<Ts...>> { };
}
template <class... Ts>
Container(Ts...) -> Container<Ts...>;
Container
通过隐式转换为 std::tuple
和 std::tuple_size
.
std::get
See it Live on Coliru(针对 C++17 之前的 GCC 进行了调整:/)