gpt4 book ai didi

c++ - 可以使用哪些方法在std::vector和pmr::vector容器之间创建接口(interface)?

转载 作者:行者123 更新时间:2023-12-01 14:36:43 27 4
gpt4 key购买 nike

我当前正在更新组件以使用pmr::vector容器,而不是std::vector。不幸的是,该组件很复杂,并且在组件之外有很多类层次结构和依赖项。此外,std::vector是其中许多接口(interface)的一部分。
因为std::vector和pmr::vector不兼容,所以我很难隔离在组件中所做的任何更新。由于组件很大,所以我想进行增量更新,但是我不能全力以赴地做到这一点,这并不是因为不费吹灰之力。
通常,我将使用适配器类并覆盖对基类的函数调用,如下所示。
class OldClass {
上市:
虚拟〜OldClass()=默认值;

虚拟std::vector DoSomething()const {
返回一些std::vector;
}
};

类NewClass {
上市:
pmr::vector DoSomething()const {
返回一些pmr::vector;
}
};

类适配器:public OldClass {
私有(private)的:
NewClass * adaptee_;

上市:
Adapter(NewClass * adaptee):Adaptee_(adaptee){}
pmr::vec DoSomething()const覆盖{
}
};

但是,我正在解决一个问题,需要为这种类型的实现制定一个清晰的用例。我看到的一个例子如下。
ComponentObjects类
{
上市:
struct ObjectParameters
{
size_t number_of_steps;
双倍时间;
};
ComponentObjects(ObjectParameters一个,ObjectParameters两个);

无效更新(const std::vector &par1,
const OtherClassTwo&par2,
const double par4,
const OtherClassThree&par5,
OtherClassFour <> * par6,
uint64_t par7,
const OtherClassFive&par8,
const OtherClassSix&par9);

const std::vector &DoSomething()const {return priv_mem_one; }

const std::vector &DoSomethingElse()const {return priv_mem_two; }

私有(private)的:
std::vector priv_mem_one {};
std::vector priv_mem_two {};
const ObjectParameter par_one_ {};
const ObjectParameter par_two_ {};
};

预先感谢您的任何帮助。

最佳答案

std::vectorpmr::vector的增量过渡的一种选择是在API上键入-擦除vector对象,而使用可转换为std::vectorpmr::vector的对象。如果此转换是隐式的,那么当您更改组件以使用pmr时,旧代码将继续工作而无需更改
您可以在任何地方简单地使用转换函数-但这可能导致需要进行许多更改,才能对每个组件进行较小的增量更改。将其隐藏在类型后面可以使旧代码在发生过渡时表现出与以前一样的行为。
精简版
如何做到这一点的简短概述是执行以下操作

  • 创建std::vectorstd::pmr::vector之间的转换函数,反之亦然
  • 创建一个包装类型:
  • 可从std::vectorstd::pmr::vector
  • 隐式构造
  • 可隐式转换为std::vectorstd::pmr::vector以及
  • 隐式使用上述转换工具来允许转换

  • 转换过渡API,以对任何函数参数和返回值使用包装类型,而不是先前的`std::vector
  • 由于此类型可以与不同的 vector 类型相互转换,因此您现有的代码应继续起作用-同时允许您将组件间迁移到

  • 一旦所有使用者都不再使用std::vector,将包装的类型改回std::pmr::vector

  • 我将在下面详细介绍。
    详细版本
    请注意,无论您执行什么过程,在过渡期间,在两者之间进行转换时,总会有某种形式的临时开销。这是因为 std::vector中的分配器与 pmr::vector中的多态分配器不同-即使它们都在后台使用 new / delete。 C++没有提供使用不同类型的分配器在 vector 之间转换数据的方法-意味着唯一的方法是为不同的 vector 分配一个新块,并从旧 vector 复制或移动每个对象。
    我必须强调,这笔费用是暂时的,因为一旦一切过渡,它就会消失。
    转换功能
    正如Mikael在回答中所建议的那样,您仍然需要转换实用程序。这些将成为自动转换对象的基础。
    我做了一个简单的转换器,仅根据 vector类型更改了 Allocator。这没有考虑pmr类型的新 memory_resource,因此您可能需要根据需要添加更多内容。
    // Conversion functions for copying/moving between vectors
    namespace detail {

    // Conversion that copies all entries (const lvalue vector)
    template <typename NewAllocator, typename T, typename OldAllocator>
    std::vector<T, NewAllocator> convert_vector(const std::vector<T, OldAllocator>& v)
    {
    auto result = std::vector<T, NewAllocator>{};
    result.reserve(v.size());
    result.assign(v.begin(), v.end());
    return result;
    }
    // conversion that moves all entries (rvalue vector)
    template <typename NewAllocator, typename T, typename OldAllocator>
    std::vector<T, NewAllocator> convert_vector(std::vector<T, OldAllocator>&& v)
    {
    auto result = std::vector<T, NewAllocator>{};
    result.reserve(v.size());
    result.assign(
    std::make_move_iterator(v.begin()),
    std::make_move_iterator(v.end())
    );
    return result;
    }
    } // namespace detail
    注意:这些转换函数只是更改 vector 中使用的分配器,并且有2个重载:一个重载每个对象,另一个重载将移动每个对象。由于我们无法移动基础 vector ,因此这是我们能做的最好的事情-这将是暂时的开销。
    包装类型
    这样,我们只需要一个简单的类型就可以在API上使用它们以某种方式对 vector 进行规范化。我们想要两个关键的事情:
  • 如果我们使std::vectorstd::pmr::vector都可以隐式构造此类型,则可以将此类型用作API上的参数-因为它可以接受。
  • 如果我们使类型隐式转换为std::vectorstd::pmr::vector,则可以在组件的返回类型上使用此类型,因为消费者可以直接为其分配值,并且它“可以正常使用”。

  • 因此,让我们创建这种类型:
    // Type erased class that can behave as either vector
    // Normalizes all vectors to a std::pmr::vector
    template <typename T>
    class AnyVector
    {
    public:

    // Implicitly constructible from both std::vector and pmr::vector

    // std::vector overloads need to convert to pmr::vector
    AnyVector(const std::vector<T>& vec)
    : m_storage{detail::convert_vector<std::pmr::polymorphic_allocator<T>>(vec)}
    {}
    AnyVector(std::vector<T>&& vec)
    : m_storage{detail::convert_vector<std::pmr::polymorphic_allocator<T>>(std::move(vec))}
    {}


    AnyVector(const std::pmr::vector<T>& vec) // no cost
    : m_storage{vec}
    {}
    AnyVector(std::pmr::vector<T>&& vec) // no cost
    : m_storage{std::move(vec)}
    {}

    AnyVector(const AnyVector&) = default;
    AnyVector(AnyVector&&) = default;

    // AnyVector& operator= for vector objects is less important, since this is meant
    // to exist on the API boundaries -- but could be implemented if there's a need.

    // Implicitly convertible to std::vector
    operator std::vector<T>() const
    {
    return detail::convert_vector<std::allocator<T>>(current);
    }
    operator std::vector<T>() &&
    {
    return detail::convert_vector<std::allocator<T>>(std::move(current));
    }

    // Implicitly convertible to std::pmr::vector
    operator std::pmr::vector<T>() const
    {
    return m_storage;
    }
    operator std::pmr::vector<T>() &&
    {
    return std::move(m_storage);
    }

    private:

    std::pmr::vector<T> m_storage;
    };
    这很简单:它是可以从 std::vectorstd::pmr::vector隐式构造的类型,也可以同时转换为两者。在内部,它保持在 std::pmr::vector上的规范化,因为这是最终目标。
    全部放在一起
    现在,您可以在要支持过渡到的API上使用它。使用问题中的代码:
    class ComponentObjects
    {
    public:
    ...

    void Update(AnyVector<OtherClass> par1,
    const OtherClassTwo& par2,
    const double par4,
    const OtherClassThree& par5,
    OtherClassFour<>* par6,
    uint64_t par7,
    const OtherClassFive& par8,
    const OtherClassSix& par9);

    AnyVector<OtherClassSeven> DoSomething() const { return priv_mem_one; }

    AnyVector<OtherClassEight> DoSomethingElse() const { return priv_mem_two; }

    private:
    std::pmr::vector<ClassA> priv_mem_one{};
    std::pmr::vector<ClassA> priv_mem_two{};
    const ObjectParameter par_one_{};
    const ObjectParameter par_two_{};
    };
    这里要注意的事情:
  • Update现在接受AnyVector,因此您可以在内部将其转换为std::pmr::vector<OtherClass>
  • 这是按值而不是const引用,因此您可以在使用代码中将此对象std::move转换为std::pmr::vector,这是真正的举措,无需转换(轻量级)
  • 消费者仍然可以使用旧的std::vector或新的std::pmr::vector调用此代码。
  • 将所有使用者迁移到std::pmr::vector后,您可以删除AnyVector并用std::pmr::vector替换

  • priv_mem_onepriv_mem_two现在是std::pmr::vector s-因为这是所需的内部结构
  • DoSomething()DoSomethingElse现在按值返回AnyVector对象。
  • 引用更便宜,但是如果std::vectorstd::pmr::vector使用者都需要这种类型的引用,那么这将保证两者都可以使用此类型。即使您选择手动转换任何地方,这也将是必要的-因为最终会在某个地方需要std::vector
  • 因为DoSomethingDoSomethingElse返回AnyVector,所以所有使用者都可以继续将其与std::vectorstd::pmr::vector一起使用。
  • 如果调用者尝试使用std::vector之类的东西,这将触发移动转换,因为返回的类型是按值(这是PR值,并触发了&&的转换过载)。
  • 如果调用者试图将其作为std::pmr::vector消费,则消费者将看到 vector 本身的移动-这是轻量级的。

  • 和上面一样,一旦所有使用者都迁移到std::pmr::vector,则可以将这些类型更改回不再是AnyVector

  • 关于c++ - 可以使用哪些方法在std::vector和pmr::vector容器之间创建接口(interface)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62393945/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com