如何改变模板的可变参数

How to mutate variadic arguments of a template

我正在尝试创建一个数组结构:

auto x = MakeMegaContainer<StructA, StructB, StructC>();

我想在编译时生成如下结构:

struct MegaContainer {
    std::tuple< Container<StructA>, Container<StructB>, Container<StructC> > Data;
};

创建方法是不可协商的,我认为元组是最好的方式,因为稍后我可以使用 std::get<StructA>(x)[index] 访问一个容器的结构元素,如果我的容器允许 []

我试过做类似的事情:

    template<typename... LilStructs>
    class StructStorage {
    public:
        template<typename OneStruct>
        OneStruct& GetStruct(const uint64_t& id) {
            return std::get<OneStruct>(m_Storage).Get(id);  // Get is defined for MyContainer
        }

        template<typename OneStruct>
        bool ContainsStruct(const uint64_t& id) {
            return std::get<OneStruct>(m_Storage).Contains(id);  // Contains is defined for MyContainer
        }

    private:
        std::tuple<MyContainer<LilStructs>...> m_Storage;
    };

但我认为这行不通。我不确定如何获取可变参数并用容器“包装”它们

我应该怎么做?

跟进问题:MyContainer 还需要额外的自定义参数,例如最大尺寸。有没有一种很好的传递方式,比如 template<typename... LilStructs, uint64_t MAX_SIZE=4096>?

这是一个精简版的容器,用于最小的可重现示例:

template<typename T_elem, typename  T_int = uint64_t, T_int MAX_SIZE = 4096>
class MyContainer{
public:
    MyContainer() = default;
    ~MyContainer() = default;

    bool Contains(const T_int& id) const;

    T_elem& operator[](const T_int& id);

    T_elem& Get(const T_int& id);

private:
    std::map<T_int, T_elem> m_Map;
    T_int m_Size = 0;
};

template<typename T_elem, typename  T_int, T_int MAX_SIZE>
bool  MyContainer<T_elem, T_int, MAX_SIZE>::Contains(
    const T_int& id
) const {
    return m_Map[id] < m_Size;
}

template<typename T_elem, typename  T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::operator[](const T_int& id) {
    return m_Map[id];
}

template<typename T_elem, typename  T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::Get(const T_int& id) {
    return operator[](id);
}

当我尝试编译时:

void Test() {
    struct SA { int x; };
    struct SB { float x; };
    struct SC { char x; };
    auto& tests = StructStorage <SA, SB, SC>();

    bool x = !tests.ContainsStruct<SA>(5);

}

我收到错误: c:\...\test.h(18): error C2039: 'Contains': is not a member of 'Trial::SA' 正如我所说,我知道错误在 std::tuple<MyContainer<LilStructs>...> m_Storage; 行中,并且错误与 MyContainer 的实现无关(前提是 ContainsGet 已实现),但我不知道用什么替换它,或者如何实现我正在寻找的功能(已经描述过了)。

看这部分:

template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
    return std::get<OneStruct>(m_Storage).Get(id);  // Get is defined for MyContainer
}

template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
    return std::get<OneStruct>(m_Storage).Contains(id);  // Contains is defined for MyContainer
}

m_Storage 元组是 MyContainer<LilStructs>... 而不是 LilStructs... 的元组,所以你真正想要做的是:

template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
    return std::get<MyContainer<OneStruct>>(m_Storage).Get(id);  // Get is defined for MyContainer
    //              ^^^^^^^^^^^^^^^^^^^^^^
}

template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
    return std::get<MyContainer<OneStruct>>(m_Storage).Contains(id);  // Contains is defined for MyContainer
    //              ^^^^^^^^^^^^^^^^^^^^^^
}

此外,您的 Contains() 函数有误。使用这个:

template <typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(const T_int& id) const {
    return m_Map.find(id) != m_Map.end();
}

完整的工作代码:

#include <cstdint>
#include <cassert>
#include <tuple>
#include <map>

template <typename T_elem, typename T_int = uint64_t, T_int MAX_SIZE = 4096>
class MyContainer{
public:
    MyContainer() = default;
    ~MyContainer() = default;

    bool Contains(const T_int& id) const;

    T_elem& operator[](const T_int& id);

    T_elem& Get(const T_int& id);

private:
    std::map<T_int, T_elem> m_Map;
    T_int m_Size = 0;
};

template <typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(const T_int& id) const {
    return m_Map.find(id) != m_Map.end();
}

template <typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::operator[](const T_int& id) {
    return m_Map[id];
}

template <typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::Get(const T_int& id) {
    return operator[](id);
}

template <typename ...LilStructs>
class StructStorage {
public:
    template <typename OneStruct>
    OneStruct& GetStruct(const uint64_t& id) {
        return std::get<MyContainer<OneStruct>>(m_Storage).Get(id);
    }

    template <typename OneStruct>
    bool ContainsStruct(const uint64_t& id) {
        return std::get<MyContainer<OneStruct>>(m_Storage).Contains(id);
    }

private:
    std::tuple<MyContainer<LilStructs>...> m_Storage;
};

int main() {
    struct SA { int x; };
    struct SB { float x; };
    struct SC { char x; };
    auto tests = StructStorage<SA, SB, SC>();

    assert(!tests.ContainsStruct<SA>(5));
}

Demo