【问题标题】:Can I reinterpret std::vector<char> as a std::vector<unsigned char> without copying?我可以在不复制的情况下将 std::vector<char> 重新解释为 std::vector<unsigned char> 吗?
【发布时间】:2017-05-18 15:53:36
【问题描述】:

我有一个对std::vector&lt;char&gt; 的引用,我想将它用作接受std::vector&lt;unsigned char&gt; 的函数的参数。我可以不复制吗?

我有以下功能,它可以工作;但是我不确定是否真的发生了副本 - 有人可以帮助我理解这一点吗?是否可以使用std::move 来避免复制或者它已经没有被复制?

static void showDataBlock(bool usefold, bool usecolor,
            std::vector<char> &chunkdata)  
{
  char* buf = chunkdata.data();                      
  unsigned char* membuf = reinterpret_cast<unsigned char*>(buf); 
  std::vector<unsigned char> vec(membuf, membuf + chunkdata.size()); 
  showDataBlock(usefold, usecolor, vec);   
} 

我在想我可以写:

std::vector<unsigned char> vec(std::move(membuf),
                               std::move(membuf) + chunkdata.size());  

这是矫枉过正吗?实际发生了什么?

【问题讨论】:

  • std::vector&lt;unsigned char&gt; vec(membuf, membuf + chunkdata.size()); 复制chunkdata中的数据
  • 基于 showDataBlock 的名称,也许可以重新设计以采用通用迭代器对
  • @WhiZTiM:不,有第二个重载(未显示)将std::vector&lt;unsigned char&gt; 作为其第三个参数。不过,我确实想知道为什么显示的重载通过非常量引用获取其第三个参数。如果另一个重载也通过非常量引用获取其参数,那么它可能会修改它,并且显示的代码无法将修改从vec复制回chunkdata。
  • @MSalters,this 中的转换是否可能会根据 OP 对该答案的 cmets 的关注而抑制某些优化?
  • @serup - 我再次编辑了标题;如果您仍然认为它没有帮助,您可以随时 edit 您自己的帖子。

标签: c++ stdvector reinterpret-cast


【解决方案1】:

...是否可以使用 std::move 来避免复制,或者它已经不是 被复制

您不能在两个不相关的容器之间移动。 std::vector&lt;char&gt; 不是 std::vector&lt;unsigned char&gt;。因此,没有合法的方法可以在 O(1) 时间内“移动 ~ 转换”一个内容到另一个内容。

你可以复制:

void showData( std::vector<char>& data){
    std::vector<unsigned char> udata(data.begin(), data.end());
    for(auto& x : udata)
        modify( x );
    ....
}

或为每次访问实时投射...

inline unsigned char& as_uchar(char& ch){
    return reinterpret_cast<unsigned char&>(ch);
}

void showDataBlock(std::vector<char>& data){
    for(auto& x : data){
        modify( as_uchar(x) );
    }
}

【讨论】:

  • 这个解决方案似乎是正确的,但是它可能在性能方面不太好,所以我决定使用另一个解决方案
  • 性能调优的第一条规则是“衡量”。虽然,对于任何好的优化编译器(显然是 Clang、GCC、MSVC、Intel 的现代版本),不应该为演员生成代码......但是,我无法评论优化影响...... MSalters answered this question几年前。他肯定更适合评论这种特殊情况是否会抑制某些优化。
  • 碰巧unsigned char&amp; 是一个特例。基本上,要使memcpy 工作,unsigned char&amp; 必须能够为任何东西起别名。所以现有的 `modify(unsigned char&)` 函数已经阻止了 as_uchar() 方法也会阻止的优化。
【解决方案2】:

如果您有std::vector&lt;T1&gt; 类型的v1 并且需要std::vector&lt;T2&gt; 类型的v2,则无法复制数据,即使 T1 和 T2 像 char 和 @ 一样“相似” 987654326@.

使用标准库:

std::vector<unsigned char> v2;
std::copy(v1.begin(), v1.end(), std::back_inserter(v2));

唯一可能的解决方法是只使用一种类型:如果可能的话,从一开始就获取std::vector&lt;T2&gt;,或者从现在开始使用std::vector&lt;T1&gt;(也许添加一个处理它的重载)。或者创建可以处理任何 [contigous] 容器的通用代码(模板)。


我认为 reinterpret_cast 和 std::move 应该可以 避免复制
不,它不能
请详细说明 - 为什么不呢?

一个向量只能从另一个相同类型的向量中窃取资源(移动数据)。它的界面就是这样设计的。

要执行您想要的操作,您需要一个 release() 方法,该方法将释放基础数据的向量所有权并将其作为(唯一)指针和一个移动构造函数/赋值返回,该函数将从 (唯一)指针。 (即使那样你仍然需要一个reinterpret_cast 这是......危险区域)

std::vector 没有这些。也许它应该有。它只是没有。

【讨论】:

  • 我认为 reinterpret_cast 和 std::move 应该可以避免复制
  • @serup 不,它不能
  • 请详细说明 - 为什么不呢?
  • @serup 向量只能从另一个相同类型的向量中窃取资源(移动数据)。它的界面就是这样设计的。要执行您想要的操作,您需要一个 release() 方法,该方法将释放基础数据的向量所有权并将其作为(唯一)指针返回,以及一个向量的移动构造函数,该构造函数将从(唯一)指针获取基础数据. std::vector 没有这些。
  • @serup 可以。但事实并非如此。正如我所说,std::vector 没有允许手动获取/释放其内部缓冲区的接口。移动到向量的唯一方法是从相同类型的std::vector。 std::vector 就是这样设计的。
【解决方案3】:

正如其他人已经指出的那样,如果不更改 showDataBlock,就无法绕过副本。

我认为你有两个选择:

  1. 将showDataBlock 扩展到signed char 和unsigned char(即使其成为模板)或
  2. 不要将容器作为参数,而是使用迭代器范围。然后你可以(如果value_type 是char)使用特殊的迭代器从signed char 转换为unsigned char 元素。

【讨论】:

  • 感谢您对扩展我的功能以同时处理两者的建议 - 我最终这样做了
【解决方案4】:

我猜你编写了另一个重载函数:-

showDataBlock(usefold, usecolor, std::vector<unsigned char> & vec);  

您尝试从std::vector&lt;T&gt; 转换为另一个std::vector&lt;T2&gt;。

没有办法避免复制。

每个std::vector都有自己的存储,粗略的说是一个原始指针。
要点是:您不能在多个std::vector 之间共享这样的原始指针。
我认为这是设计使然。
我认为这是一件好事,否则会浪费CPU来跟踪。

代码...

std::move(membuf)

... 移动原始指针 = 实际上什么也不做。 (和membuf一样传递)

要进行优化,您应该首先验证原因:为什么要从std::vector&lt;char&gt; 转换为std::vector&lt;unsigned char&gt;。

如果您创建一个可以表示为char 和unsigned char 的新类C 是不是更好? (例如C::getChar()和C::getUnsignedChar(),可能是……只存储char,但提供转换器作为其非静态函数)

如果没有帮助,我建议创建一个新的自定义数据结构。
我经常在需要时这样做。

但是,在这种情况下,我认为它不需要任何优化。
对我来说没问题,除了它是性能关键代码。

【讨论】:

  • 这是设计使然。想共享存储的时候有std::shared_ptr,可以将两者结合起来:std::shared_ptr&lt;std::vector&lt;char&gt;&gt;
  • @MSalters 好点先生! std::shared_ptr&lt;std::vector&lt;char&gt;&gt; 是一个很酷的符号。 ……“这是设计使然。” shared_ptr<vector<T>>)?你封装它吗?我好奇。 :)
  • 我不记得具体的案例,但它根本不是异国代码。例如。当你有一个发送者和一个接收者,并且它们之间共享缓冲区时,你会期望接收者有一个std::shared_ptr&lt;vector&lt;T&gt;&gt;,而发送者有一个std::weak_ptr&lt;std::vector&lt;T&gt;&gt;。 (接收方退出后,发送方不需要让缓冲区保持活动状态)
【解决方案5】:

而unsigned char 和char 是不相关的类型。我认为在这种情况下它们足够相似(相同大小的 pod),可以摆脱整个模板类的 reinterpret_cast。

static void showDataBlock(bool usefold, bool usecolor,
            std::vector<char> &chunkdata)  
{
  showDataBlock(usefold, usecolor, reinterpret_cast< std::vector<unsigned char>&>(chunkdata));   
}

但是,我倾向于发现这些问题是由于没有设计出最好的架构。看看这个软件应该做什么的大局,以确定为什么您需要同时处理有符号和无符号字符数据块。

【讨论】:

    【解决方案6】:

    我最终做了这样的事情:

    static void showDataBlock(bool usefold,bool usecolor, std::vector<char> chunkdata)
    {                                                                                                                           
        std::vector<unsigned char>&cache = reinterpret_cast<std::vector<unsigned char>&>(chunkdata);                                              
        showDataBlock(usefold, usecolor, cache);    
    }                                                                             
    
    static bool showDataBlock(bool usefold,bool usecolor, std::vector<unsigned char> &chunkdata)   
    {
        // showing the data
    }
    

    此解决方案允许我将向量作为参考或正常传递 它似乎正在工作 - 如果它是我不知道的最佳解决方案,但是你们都提出了一些非常好的建议 - 谢谢大家

    我同意我不能避免复制,所以我让复制通过正常的参数传递来完成

    如果您发现此解决方案有误,请在评论中提供更好的解决方案,而不仅仅是投反对票

    【讨论】:

    • 这个解决方案是基于其他人的建议结合反复试验,所以它是一个严肃的解决方案并且有效
    • 如果您投反对票,请写下原因 - 否则您并不认真
    • std::vector&lt;char&gt; 和 std::vector&lt;unsigned char&gt; 是 2 个类完全不相关(即使从同一个模板生成)。 reinterpret_cast 它们之间是未定义的行为。
    • @bolov,未定义的行为与 ref 指针的过去方式有关吗??
    猜你喜欢
    • 2012-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 2021-07-11
    • 2014-02-19
    • 2013-11-18
    • 1970-01-01
    相关资源
    最近更新 更多