如何扩展一个非值参数包

How to expand a non value paramter pack

我最近一直在尝试用 C++ 扩展一个非值参数包。这可能吗?如果不是,为什么?

我的意思是,如您所见,在注释 // 的行中,给定 TypeMap class 的参数包,我如何调用 addType<T>() 与每种类型的参数包?提前致谢!

template <typename... T>
class TypeMap
{
    using vari_cmps = std::variant<T*...>;

    private:
        template<typename Type>
        void addType()
        {
            typemap[typeid(Type).name()] = std::make_unique<Type>(0).get();
        }

    public:
        std::map<const char*, vari_cmps> typemap{};

        TypeMap()
        {
            (addType<T,...>()); // Idk how to use this in order to make it work
        }

        ~TypeMap()
        {
            typemap.clear();
        }
};

@HolyBlackCat 已经在评论中回复了,可以这样展开:

TypeMap() {
    (addType<T>(), ...);
}

如果 T 是 std::string, int, float 这将扩展为:

TypeMap() {
    (addType<std::string>(), addType<int>(), addType<float>());
}

然而,此代码片段中还有一些问题:

1。添加类型()

addType() 不会像您预期的那样工作,因为 unique_ptr 在您将对象放入地图后将其删除。

.get() 仅检索 unique_ptr 管理的指针,但 不会 转移所有权,因此 unique_ptr 仍会删除指向的指针-一旦它超出范围就对象,在你的地图中留下一个悬空的指针。

所以你的 addType() 大致相当于:

template<typename Type>
void addType() {
    Type* tptr = new Type(0); // unique pointer constructs object
    typemap[typeid(Type).name()] = tptr; // insert pointer value of unique pointer
    delete tptr; // unique pointer destructs
}

您可以通过在将 unique_ptr 的值插入映射然后在析构函数中清理它之后释放 unique_ptr 来解决此问题:

template<typename Type>
void addType() {
    auto ptr = std::make_unique<Type>(0);
    typemap[typeid(Type).name()] = ptr.get();
    ptr.release(); // now unique_ptr won't delete the object
}

~TypeMap() {
    // cleanup all pointers
    for(auto& [name, ptrVariant] : typemap)
        std::visit([](auto ptr) { delete ptr; }, ptrVariant);
}

2。考虑使用 std::type_index 而不是 const char* 作为映射键

std::type_info::name() returns 给定类型的实现定义名称,因此您不能保证给定类型的名称是唯一的。

Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given; in particular, the returned string can be identical for several types and change between invocations of the same program.

另一方面,

std::type_index 是专门为此目的构建的 - 使用类型作为键 - 并带有所有比较运算符和 std::hash 专业化,因此您可以将它与 [=27 一起使用=] & std::unordered_map 开箱即用。

例如:

template <class... T>
class TypeMap
{
    using vari_cmps = std::variant<T*...>;

    private:
        template<typename Type>
        void addType()
        {
            typemap[std::type_index(typeid(Type))] = /* something */;
        }

    public:
        std::map<std::type_index, vari_cmps> typemap{};

        TypeMap() { /* ... */ }
        ~TypeMap() { /* ... */ }

        template<class U>
        U* get() {
            auto it = typemap.find(std::type_index(typeid(U)));
            return std::get<U*>(it->second);
        }
};

考虑使用 std::tuple

std::tuple 基本上是为这个任务构建的,存储任意类型的列表:

例如:

template <class... T>
class TypeMap
{
private:
    std::tuple<std::unique_ptr<T>...> values;
public:
    TypeMap() : values(std::make_unique<T>(0)...) {
    }

    template<class U> requires (std::is_same_v<U, T> || ...)
    U& get() { return *std::get<std::unique_ptr<U>>(values); }

    template<class U> requires (std::is_same_v<U, T> || ...)
    U const& get() const { return *std::get<std::unique_ptr<U>>(values); }
};

用法:

TypeMap<int, double, float> tm;
tm.get<int>() = 12;

如果需要,您也可以将 T 直接存储在元组中,避免额外的分配。