英文:
Capturing types from parameter pack and creating an array of unique_ptr from them
问题
- 如何编写这段代码?
template<typename... TComponents>
class ComponentContainer {
using ComponentPtr = std::unique_ptr<Component>;
public:
ComponentContainer()
{
// Initialize m_components with unique_ptrs to instances of TComponents
initializeComponents();
}
private:
std::array<ComponentPtr, sizeof...(TComponents)> m_components;
// Helper function to initialize m_components with unique_ptrs to instances of TComponents
void initializeComponents()
{
// Initialize components using a fold expression
((m_components[i] = std::make_unique<TComponents>()), ...);
}
};
- 有没有办法验证所有提供的
TComponents
都是派生自Component
类型?
// 使用SFINAE (Substitution Failure Is Not An Error) 检查每个TComponent是否派生自Component
template<typename... TComponents>
class ComponentContainer {
static_assert((std::is_base_of<Component, TComponents>::value && ...), "All TComponents must be derived from Component");
// ... 其余代码同上 ...
};
这段代码使用了 static_assert
来验证 TComponents
是否都派生自 Component
类型。如果有任何一个 TComponent
不满足这个条件,编译将会失败,并显示相应的错误消息。
英文:
I want to build a container class for my Component
instances. Component is an abstract class and every type in the parameter pack should be derived from it.
template<typename... TComponents>
class ComponentContainer {
using ComponentPtr = unique_ptr<Component>;
public:
ComponentContainer()
{
}
private:
std::array<ComponentPtr, sizeof...(TComponents)> m_components;
};
The container should hold an array of pointers to the instances of each types provided.
So for every type there has to be m_components[i] = std::make_unique<TComponents>
().
- How can I write it in code?
- Is there a way to validate that all the provided
TComponents
are derived fromComponent
type?
答案1
得分: 3
"Is there a way to validate that all the provided TComponents
are derived from Component type?" can be translated to:
"是否有一种方法可以验证所有提供的 TComponents
是否都是从 Component 类型派生的?"
Please let me know if you need any further assistance.
英文:
> How can I write it in code?
You could add a constructor that moves or copies the components supplied to your array using std::make_unique
:
#include <utility> // std::forward
template <typename... TComponents>
class ComponentContainer {
using ComponentPtr = std::unique_ptr<Component>;
public:
ComponentContainer() // default construct components
: m_components{std::make_unique<
std::remove_cv_t<std::remove_reference_t<TComponents>>>()...} {}
template <class... Args>
ComponentContainer(Args&&... comp) // move/copy construct components
: m_components{std::make_unique<
std::remove_cv_t<std::remove_reference_t<TComponents>>>(
std::forward<Args>(comp))...} {}
private:
std::array<ComponentPtr, sizeof...(TComponents)> m_components;
};
// deduction guide
template <class... Args>
ComponentContainer(Args...) ->
ComponentContainer<std::remove_cv_t<std::remove_reference_t<Args>>...>;
Making TComponents...
part of the type can be problematic though. If an element in the array was initialized as the derived type C1
, it will be odd if you later make the unique_ptr
point at a C2
. The template parameters and the contained object types won't match anymore.
Also, if you specify the template parameters explicitly, you may make them const
, but the container itself can't mix non-const
and const
Component
pointers, so they'll have to be non-const
.
For both those reasons, you should probably not let users get non-const
access to the individual unique_ptr
s directly.
You could add tuple
-like get
member functions to get a reference to the contained type with the correct const
qualification:
// helper trait to get the specific type out of a template parameter pack
template <size_t I, class... Args>
using type_in_pack_t = std::tuple_element_t<I, std::tuple<Args...>>;
// ...
template <std::size_t I>
const auto& get() const { // definite const&
return *static_cast<type_in_pack_t<I, TComponents...>*>(
m_components[I].get());
}
template <std::size_t I>
decltype(auto) get() { // non-const& / const& depending on template params
return *static_cast<type_in_pack_t<I, TComponents...>*>(
m_components[I].get());
}
It's often more useful to not make the concrete types of the components used to initialize the class template be a part of the final type. Since you store the elements in an array
, the size of the array could be the template parameter instead:
template <size_t N>
class ComponentContainer {
using ComponentPtr = std::unique_ptr<Component>;
public:
ComponentContainer() = default;
template <class... Args>
ComponentContainer(Args&&... comp) // move/copy construct components
: m_components{
std::make_unique<std::remove_cv_t<std::remove_reference_t<Args>>>(
std::forward<Args>(comp))...} {}
private:
std::array<ComponentPtr, N> m_components{};
};
// deduction guide
template <class... Args>
ComponentContainer(Args...) -> ComponentContainer<sizeof...(Args)>;
> Is there a way to validate that all the provided TComponents
are derived from Component type?
You'll get a compilation error if a pointer to one of the supplied TComponents
(after remove_const_t
) is not implicitly convertible to a Component*
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论