【问题标题】:C++ class wrapper and placement newC++ 类包装器和放置新
【发布时间】:2023-03-30 21:50:01
【问题描述】:

我有一个模板容器类,它需要将附加信息与用户指定类型的每个实例关联起来。

容器指定一个包装结构,包含指定的类型和附加信息...

template<typename T> class Container
{
    ...

    struct Wrapper
    {
        T   mType;
        int mInfo;
    };
}

在初始化时,容器会填充动态分配的 Wrapper 实例(即 Wrapper*)。用户可以 pop() 一个实例,容器将返回一个转换为 T*。用户可以 push() 一个实例,容器将把它转换回 Wrapper*。

如果可以默认构造数据类型,则此方法可以正常工作。然而,情况并非总是如此,我想让用户提供一个分配函数,在初始化期间调用,它可以构造它喜欢的数据类型。容器要求函数的特定签名是可以的。

template<typename T> template<typename F> void Container<T>::init(F allocator);

我正在努力解决的是如何在函数分配数据类型时分配 Wrapper。我考虑过使用placement new,容器为Wrapper分配足够的空间,并将地址传递给函数来做一个新的数据类型的placement。

有没有更清洁的方法来解决这个问题?

【问题讨论】:

  • 最好写代码,然后解释类应该做什么。如果可以推送/弹出,为什么要创建实例?
  • 只能推送/弹出属于容器的对象。也就是说,用容器需要的附加信息装饰的对象。用户看到 T*,而容器看到 Wrapper*。
  • 我同意 BЈовић:最好还是写代码。但是,据我所知,您需要 Wrapper 中的构造函数来获取您的 T.
  • 为什么不将Wrapper::mType 设为指向T 的指针?
  • 是的,考虑过让 Wrapper 对 T 进行复制构造,但不想要求 T 是可复制构造的。

标签: c++ wrapper placement-new


【解决方案1】:

您不需要为此分配功能。相反,您需要标准容器中的 emplace_push 之类的东西。

【讨论】:

    【解决方案2】:

    我只是将用于构造 T 的参数添加为 Container 的模板参数,并使用它们来构造 T。为了允许动态构造 T 对象,容器构造函数接受一个指向函数的指针作为参数参数为创建等级的整数,T构造函数的默认参数,并返回T构造函数的参数元组(std::tuple&lt;U...&gt; (*getInitParam)(int rank, U... params)):

    template<typename T, typename ...U>
    class Container {
        struct Wrapper {
            T mType;
            int mInfo;
    
            Wrapper(int mInfo, U... params): mType(params...), mInfo(mInfo) {}
        };
    
        std::stack<Wrapper *> stack;
    
        int get_mInfo() {
            static int info = 0;
    
            return info++;
        }
        std::tuple<U...> (*getInitParam)(int rank, U... params);
    
    public:
        T* pop() {
            // should control stack is not empty
            Wrapper *w = stack.top();
            stack.pop();
            return &w->mType;
        }
        void push(T* type) {
            char *p = reinterpret_cast<char *>(type);
            p -= offsetof(Wrapper, mType);
            Wrapper *w = reinterpret_cast<Wrapper *>(p);
            // would need to control that *w is a valid Wrapper
            stack.push(w);
        }
        void init(int n, U... params) {
            std::tuple<U...> orig = std::make_tuple(params...);
            for (int i=0; i<n; i++) {
                if (getInitParam != nullptr) {
                    std::tie(params...) = orig;
                    std::tuple<U...> tparams = (*getInitParam)(i, params...);
                    std::tie(params...) = tparams;
                }
                Wrapper *w = new Wrapper(get_mInfo(), params...);
                stack.push(w);
            }
        }
        Container(std::tuple<U...> (*getInitParam)(int rank, U... params) = nullptr)
            : getInitParam(getInitParam) {}
        ~Container() {
            while(! stack.empty()) {
                Wrapper *w = stack.top();
                delete w;
                stack.pop();
            }
        }
    };
    

    使用示例:

    std::tuple<size_t, char> doInit(int i, size_t szmin, char c) {
        return std::make_tuple(szmin + i, c);
    }
    
    int main() {
        Container<std::string, size_t, char> cont(&doInit);
        cont.init(10, 5, 'x');
    
        std::string* str0 = cont.pop();
        std::string* str = cont.pop();
        std::cout << *str0 << std::endl;
        std::cout << *str << std::endl;
    
        *str0 = "bar";
    
        cont.push(str0);
        cont.push(str);
    
        str0 = cont.pop();
        str = cont.pop();
    
        std::cout << *str << std::endl;
        cont.push(str);
        cont.push(str0);
        return 0;
    }
    

    这正确输出:

    xxxxxxxxxxxxxx
    xxxxxxxxxxxxx
    bar
    

    还有要实现mInfo的使用,不过应该是个起点……

    【讨论】:

    • 是的,如果 T 的构造函数参数是静态的,则此方法有效。考虑一个简单的情况,即 T 的每个实例都需要一个唯一的 ID 作为其构造函数的一部分。这就是为什么我希望用户能够传入某种分配器。
    • @user1715664:我认为我可以通过一个计算用于 T 对象构造的参数的可选函数来满足该要求。查看编辑后的帖子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2016-12-29
    相关资源
    最近更新 更多