首页 > 解决方案 > 如何改变模板的可变参数

问题描述

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

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(前提是Contains并且Get已执行),但我不知道用什么替换它,或者如何实现我正在寻找的功能(已经描述过)。

标签: c++c++17variadic-templates

解决方案


看这部分:

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>...s 而不是s的元组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


推荐阅读