当前位置: 首页 > 知识库问答 >
问题:

可以使用什么方法来创建STD::Vector和PMR::Vector容器之间的接口?

商皓
2023-03-14

我当前正在更新一个组件,以使用PMR::Vector容器,而不是STD::Vector。不幸的是,组件是复杂的,并且在组件外部有大量的类层次结构和依赖关系。此外,std::vector是其中许多接口的一部分。

因为std::vector和pmr::vector不兼容,所以我很难隔离组件中的任何更新。由于组件有点大,我想进行增量更新,但我不能考虑一个好的方法来这样做,这并不是因为缺乏努力。

通常,我会使用适配器类并重写对基类的函数调用,如下所示。

class OldClass {
 public:
  virtual ~OldClass() = default;

  virtual std::vector DoSomething() const {
    return some std::vector;
  }
};

class NewClass {
 public:
  pmr::vector DoSomething() const {
    return some pmr::vector;
  }
};

class Adapter : public OldClass {
 private:
  NewClass *adaptee_;

 public:
  Adapter(NewClass *adaptee) : adaptee_(adaptee) {}
  pmr::vec DoSomething() const override {
  }
};
class ComponentObjects
{
  public:
    struct ObjectParameters
    {
        size_t number_of_steps;
        double time;
    };
    ComponentObjects(ObjectParameters one, ObjectParameters two);

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

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

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

  private:
    std::vector<ClassA> priv_mem_one{};
    std::vector<ClassA> priv_mem_two{};
    const ObjectParameter par_one_{};
    const ObjectParameter par_two_{};
};

事先谢谢你的帮助。

共有1个答案

尉迟卓
2023-03-14

std::vectorpmr::vector的增量转换的一个选项是键入擦除API上的vector对象,而使用一个既可转换为std::vector又可转换为pmr::vector的对象。如果此转换是隐式的,则当您更改组件以使用PMR时,旧代码将继续工作而不作更改

您可以简单地在任何地方使用一个转换函数--但这可能导致在每个组件上进行较小的增量更改所需的大量更改。将其隐藏在类型之后,使得在转换发生时,旧代码的行为与以前一样。

关于如何实现这一点的一个简短的提纲是执行以下操作

  • std::vectorstd::pmr::vector之间创建转换函数,反之亦然
  • 创建包装类型,该包装类型:
    • 可以从std::vectorstd::pmr::vector中隐式构造,
    • 隐式可转换为std::vectorstd::pmr::vector以及
    • 隐式使用上述转换实用工具以允许转换
    • 由于此类型可以与不同的向量类型进行转换,所以现有的代码应该继续工作--同时允许您进行组件到组件的迁移

    我将在下面更详细地讨论这一点。

    我必须强调,这种代价是暂时的,因为一旦一切都过渡过去,它就会消失。

    您仍然需要转换实用程序,正如Mikael在他的回答中所建议的那样;这些将为自动转换对象奠定基础。

    我制作了一个简单的转换器,它只需根据allocator类型更改vector。这没有考虑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
    

    有了这个,我们只需要一个简单的类型,我们可以在API上使用它,以某种方式对向量进行规范化。我们需要两个关键的东西:

    • 如果我们使此类型可以从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_{};
    };
    

    这里需要注意的事项:

      null
      null

 类似资料:
  • 要检查向量是否为空,我可以使用或。我查看了cplike上的签名,但缺乏理解它们的知识。它们如何相互关联?一个实现调用另一个实现吗? 我知道其中一个来自容器库,另一个来自迭代器库,但仅此而已。

  • https://godbolt.org/z/P97MaK 我玩的概念和预期d::is_equality_comparable工作矢量,但它没有。 编译错误在 内部失败,而不是在受概念保护的函数边界处失败。 这是错误还是预期行为?

  • 我以前见过这样做,但我不记得如何有效地初始化已知长度的与长度相同的。这里有一个很好的例子: 我已经仔细阅读了这一页关于高级矩阵初始化的内容,但是没有明确解释执行此操作的方法。

  • 下面的代码显示了我要做的:

  • 错误:无法将类型为“std::_bit_reference&”的非常量lvalue引用绑定到类型为“std::vector::reference”{aka“std::_bit_reference”}的rvalue 因此,它抱怨,因为只有第二个参数是rvalue

  • 我有一个整数向量: 考虑到将始终为偶数。 我只是想把相邻的元素转换成一对,像这样: 即,两个相邻元件接合成一对。 我可以使用什么STL算法轻松实现这一点?有没有可能通过一些标准算法来实现这一点? 当然,我可以很容易地编写一个旧的索引for循环来实现这一点。但我想知道最简单的解决方案是什么,使用rangebased for循环或任何其他STL算法,比如,等等。