从参数包中捕获类型并创建一个包含它们的unique_ptr数组。

huangapple go评论64阅读模式
英文:

Capturing types from parameter pack and creating an array of unique_ptr from them

问题

  1. 如何编写这段代码?
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>()), ...);
    }
};
  1. 有没有办法验证所有提供的 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&lt;typename... TComponents&gt;
class ComponentContainer {

	using ComponentPtr = unique_ptr&lt;Component&gt;;
public: 
	ComponentContainer() 
	{
		 
	}

	 
private:
	std::array&lt;ComponentPtr, sizeof...(TComponents)&gt; 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&lt;TComponents&gt;().

  1. How can I write it in code?
  2. Is there a way to validate that all the provided TComponents are derived from Component 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 &lt;utility&gt; // std::forward

template &lt;typename... TComponents&gt;
class ComponentContainer {
    using ComponentPtr = std::unique_ptr&lt;Component&gt;;

public:
    ComponentContainer()  // default construct components
        : m_components{std::make_unique&lt;
              std::remove_cv_t&lt;std::remove_reference_t&lt;TComponents&gt;&gt;&gt;()...} {}

    template &lt;class... Args&gt;
    ComponentContainer(Args&amp;&amp;... comp)  // move/copy construct components
        : m_components{std::make_unique&lt;
              std::remove_cv_t&lt;std::remove_reference_t&lt;TComponents&gt;&gt;&gt;(
              std::forward&lt;Args&gt;(comp))...} {}
private:
    std::array&lt;ComponentPtr, sizeof...(TComponents)&gt; m_components;
};

// deduction guide
template &lt;class... Args&gt;
ComponentContainer(Args...) -&gt;
    ComponentContainer&lt;std::remove_cv_t&lt;std::remove_reference_t&lt;Args&gt;&gt;...&gt;;

Demo

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_ptrs 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 &lt;size_t I, class... Args&gt;
using type_in_pack_t = std::tuple_element_t&lt;I, std::tuple&lt;Args...&gt;&gt;;

// ...

    template &lt;std::size_t I&gt;
    const auto&amp; get() const {  // definite const&amp;
        return *static_cast&lt;type_in_pack_t&lt;I, TComponents...&gt;*&gt;(
            m_components[I].get());
    }

    template &lt;std::size_t I&gt;
    decltype(auto) get() {  // non-const&amp; / const&amp; depending on template params
        return *static_cast&lt;type_in_pack_t&lt;I, TComponents...&gt;*&gt;(
            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 &lt;size_t N&gt;
class ComponentContainer {
    using ComponentPtr = std::unique_ptr&lt;Component&gt;;

public:
    ComponentContainer() = default;

    template &lt;class... Args&gt;
    ComponentContainer(Args&amp;&amp;... comp)  // move/copy construct components
        : m_components{
              std::make_unique&lt;std::remove_cv_t&lt;std::remove_reference_t&lt;Args&gt;&gt;&gt;(
                  std::forward&lt;Args&gt;(comp))...} {}

private:
    std::array&lt;ComponentPtr, N&gt; m_components{};
};

// deduction guide
template &lt;class... Args&gt;
ComponentContainer(Args...) -&gt; ComponentContainer&lt;sizeof...(Args)&gt;;

Demo

> 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*.

huangapple
  • 本文由 发表于 2023年2月27日 07:26:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75575635.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定