【问题标题】:Interpret a std::string as a std::vector of char_type?将 std::string 解释为 char_type 的 std::vector?
【发布时间】:2016-01-06 16:50:07
【问题描述】:

我有一个带有const vector<T>& 的template<typename T> 函数。在所述函数中,我有向量 cbegin()、cend()、size() 和 operator[]。 据我了解,string 和 vector 都使用连续空间,所以我想知道是否可以以优雅的方式重用这两种数据类型的函数。

可以将std::string 重新解释为(适当的)char_type 的std::vector 吗?如果是这样,限制是什么?

【问题讨论】:

  • 这就是创建迭代器的目的。
  • 您是否考虑过将函数设为模板?
  • 依赖类的底层行为而不是发布的接口可能是危险的。这有什么需要?

标签: c++ c++11 vector array-view


【解决方案1】:

如果您只为类型 const T& 制作模板并使用矢量和字符串共享的 begin()、end() 等函数,那么您的代码将适用于这两种类型。

【讨论】:

  • 是否有任何实现共享生成代码(而不仅仅是源代码代码)?
  • @6502:生成的代码不会被共享,除非标准库作者为了实现它而付出了极大的努力。但你真的为什么在乎?优化后,迭代器和 operator[] 操作分别编译为几条机器指令。这没什么大不了的。
  • @ZanLynx:整个函数将被复制,而不仅仅是[] 访问代码。这可能是一个很大的功能......
  • @6502 质量链接器将消除相同的功能
【解决方案2】:

采用 STL 方式并使用迭代器。接受迭代器开始和迭代器结束。它适用于所有可能的容器,包括流等非容器。

【讨论】:

    【解决方案3】:

    不保证string 和vector 的布局会相同。它们理论上可能是,但它们可能不在任何常见的实现中。因此,您不能安全地执行此操作。请参阅 Zan 的答案以获得更好的解决方案。

    让我解释一下:如果我是标准库实现者并决定像这样实现 std::string ......

    template ...
    class basic_string {
    public:
        ...
    private:
        CharT* mData;
        size_t mSize;
    };
    

    并决定像这样实现 std::vector ...

    template ...
    class vector {
    public:
        ...
    private:
        T* mEnd;
        T* mBegin;
    };
    

    当您reinterpret_cast<string*>(&myVector) 时,您最终会将指向数据末尾的指针解释为指向数据开头的指针,并将指向数据开头的指针解释为数据的大小。如果成员之间的填充不同,或者有额外的成员,它也可能变得更奇怪和更破碎。

    所以是的,为了让它可能工作,他们都需要存储连续的数据,但他们还需要在实现之间保持相同的其他一些东西才能工作。

    【讨论】:

    • string 和 vector 都使用连续数组来存储数据。这意味着可以使用仅获取内存地址的非模板函数。
    • @AndreyNasonov 你错了。更新了答案来解释它。在你理解之前请不要投反对票:(
    • 我不是在谈论字段布局和特定实现。我说的是数据表示。 string 和 vector 都提供了指向第一个元素的 data() 函数。保证它是一块连续的内存。
    • @AndreyNasonov 你是说在reinterpret_cast<string*>(&myVector) 上调用某些函数会起作用吗?我不太清楚你在说什么,但那是不对的。是的,它们都指向连续的数据……这并没有改变我所说的……
    • 函数签名(const T *begin, const T *end)和调用f(v.data(), v.data() + v.size())有什么问题?我只考虑返回data() 方法的内容。对于vector 和string,它返回相同的结果。
    【解决方案4】:

    std::experimental::array_view<const char> n4512 表示一个连续的字符缓冲区。

    写your own is not hard,它解决了这个问题以及(根据我的经验)更多。

    字符串和向量都与数组视图兼容。

    这使您可以将您的实现移动到.cpp 文件中(而不是公开它),为您提供与使用std::vector<T> const& 相同的性能并且可能相同的实现,避免重复代码,并使用轻量级连续缓冲区类型擦除(其中充满了美味的关键字)。

    【讨论】:

      【解决方案5】:

      如果关键是您想访问内存中存储特定字符类型实例的连续区域,那么您可以将函数定义为

      void myfunc(const CType *p, int size) {
           ...
      }
      

      为了清楚表明您假设它们在内存中必须是相邻的。

      然后例如传递向量的内容,代码很简单

      myfunc(&myvect[0], myvect.size());
      

      对于一个字符串

      myfunc(mystr.data(), mystr.size());
      

      或

      myfunc(buffer, n);
      

      对于一个数组。

      【讨论】:

      • 这种方法有一个优点:它不使用模板。但是请把int改成size_t。
      • @SergeyA:编写模板(在我知道的实现中)将共享源代码代码,为不同类型生成不同的代码。
      • @AndreyNasonov,为什么这是一个优势?在我看来,这是一个缺点。
      • @6502,你指的是代码膨胀?它被夸大了。尤其是在内联的世界中。
      • @6502,这完全是错误的。我的意思是,尺寸。 int 通常为 32 位类型,size_t 为 32 或 64,具体取决于指针大小。使用 int 大小的代码很容易出错。
      【解决方案6】:

      您不能直接将 std::vector 类型转换为 std::string,反之亦然。但是使用 STL 容器提供的迭代器确实允许您以相同的方式迭代向量和字符串。如果您的函数需要随机访问相关容器,那么任何一个都可以工作。

      std::vector<char> str1 {'a', 'b', 'c'};
      std::string str2 = "abc";
      
      template<typename Iterator>
      void iterator_function(Iterator begin, Iterator end)
      {
        for(Iterator it = begin; it != end; ++it)
        {
          std::cout << *it << std::endl;
        }
      }
      
      iterator_function(str1.begin(), str1.end());
      iterator_function(str2.begin(), str2.end());
      

      最后两个函数调用都会打印相同的内容。

      现在,如果您想编写一个仅解析仅存储在字符串或向量中的字符的通用版本,您可以编写一些迭代内部数组的东西。

      void array_function(const char * array, unsigned length)
      {
        for(unsigned i = 0; i < length; ++i)
        {
          std::cout << array[i] << std::endl;
        }
      }
      

      这两个函数在以下场景中会做同样的事情。

      std::vector<char> str1 {'a', 'b', 'c'};
      std::string str2 = "abc";
      
      iterator_function(str1.begin(), str1.end());
      iterator_function(str2.begin(), str2.end());
      array_function(str1.data(), str1.size());
      array_function(str2.data(), str2.size());
      

      解决问题总是有多种方法。根据您可用的内容,任何数量的解决方案都可能有效。尝试两者,看看哪个更适合您的应用程序。如果您不知道迭代器类型,那么 char 类型的数组迭代很有用。如果你知道你总是需要传入模板类型,那么模板迭代器方法可能会更有用。

      【讨论】:

        【解决方案7】:

        您现在提出问题的方式有点令人困惑。如果您要问“如果向量恰好包含适当类型的 char 值,将 std::vector 类型转换为 std::string 类型是否安全?”,答案是:不可能,不要想都别想!如果你问:“如果它们是std::vector 或std::string 类型,我可以访问char 类型的非空序列的连续内存吗?”那么答案是,可以(使用data() 成员函数)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-11
          • 1970-01-01
          相关资源
          最近更新 更多