【问题标题】:C++: attempt to eliminate raw loop with equivalent STL algorithmC++:尝试使用等效的 STL 算法消除原始循环
【发布时间】:2018-10-10 21:25:48
【问题描述】:

我正在尝试对一些 C++ 代码进行现代化改造,遵循核心准则并发布 ++11 建议。我在这里提出的具体指导方针是使用<algorithm> 设施来代替原始循环,在序列中应用静态操作以生成新序列。

第一个示例说明了成功(正如我在此上下文中定义的那样)。 std::byte 的两个输入向量进来,一个出来,表示每个输入向量的成对按位异或,输入向量保持不变。本题的功能是std::transform.

vector<byte> XORSmash(const vector<byte>& first, const vector<byte>& second)
{
    if (first.size() != second.size())
        throw std::invalid_argument("XORSMASH: input vectors were not of equal length\n");

    vector<byte> convolution; convolution.reserve(first.size());

    transform(first.cbegin(), first.cend(), second.cbegin(), back_inserter(convolution),
        [](const byte byte1, const byte byte2) {return byte1 ^ byte2;} );

    return convolution;
}

但是,我在设计一个非循环解决方案时遇到了另一个问题,该解决方案并不比循环解决方案差。这个函数接受一个HexChars的string(每个char最终传送4位值),并生成一个vector&lt;byte&gt;,每个元素包含两个HexChars的内容,一个在高4位,一个在低。 CharToHexByte 函数的作用并不相关(如果有必要我会包括在内),只是它接受一个兼容的十六进制字符,并返回一个 std::byte,带有十六进制字符的数值,即 0-15,加载只有4位。 问题是输入字符串有成对的十六进制字符(每个字符都是半字节值),每个字符都统一为一个十六进制字节。据我所知,我不能使用 std::transform,因为输入迭代器每次迭代都必须跳 2 (2 * sizeof(char)//aka container_const_iterator += 2 in this case),以提取输入字符串中的下一个 pair 字符。

TLDR:有没有一种算法ic 方法来实现以下功能,而没有暴露的for 循环,这并不比下面的解决方案更昂贵/更冗长?

vector<byte> UnifyHexNibbles(const string& hexStr)
{
    if (hexStr.size() % 2)
        throw std::invalid_argument("UnfyHxNbl: Input String Indivisible by 8bits. Pad if applicable.\n");

    vector<byte> hexBytes; hexBytes.reserve(hexStr.size() >> 1);
    //can I be eliminated elegantly?
    for (size_t left(0), right(1); right < hexStr.size(); left += 2, right += 2)
        hexBytes.push_back( CharToHexByte(hexStr[left]) << 4 | CharToHexByte(hexStr[right]) );

    return hexBytes;
}

【问题讨论】:

  • 您可以调整迭代器,使其在递增时增加一个以上:stackoverflow.com/questions/5685983/skipping-iterator
  • @NathanOliver 我想到了这一点,但要提升或专门使用迭代器 IMO,两者都被归类为比一个线性循环更冗长。如果这种模式无处不在,那么适应/第 3 方解决方案将更具吸引力。不过,我只是在真空中看待这个案例。
  • 不用担心。我只是想介绍一下这个选项,以防你不知道。
  • 位运算、十六进制转换...对我来说似乎是一个很好的旧循环伙伴。

标签: c++ algorithm loops stl cpp-core-guidelines


【解决方案1】:

range-v3 会是

std::vector<std::byte>
UnifyHexNibbles(const std::string& hexStr)
{
    if (hexStr.size() % 2)
        throw std::invalid_argument("size indivisible by 2.");


    return hexStr
        | ranges::view::chunk(2)
        | ranges::view::transform([](const auto& r)
           {
              return std::byte(CharToHexByte(r[0]) << 4 | CharToHexByte(r[1]));
           });
}

Demo

【讨论】:

  • 我觉得这类似于为了避免向右转而向左转三圈。这仍然存在与循环相关的漏洞,通过基于大小(块视图)的常量下标,能够在不修改另一个的情况下更改一个。有点迂腐,它也有更多的文字。最后,尽管范围适配器实现了延迟执行,这从最小化副本/中间体的角度来看非常棒,但很难相信这个范围适配器链将接近原始循环的性能(基于印象而不是实现知识)。
  • 另一方面,这个答案增加了我的信心,我的原始问题的答案只是“不,具体情况太具体,无法由任何 stl 算法处理,该算法针对一般情况进行了优化情况下,连续消费一个序列。
  • " 有点迂腐,它也有更多的文字。"。是的,因为我添加了很多间距,但字符/符号比原始字符/符号少:-)(而且我是完全限定的名称)(老实说,我发现范围版本更容易阅读)。然后我会想要一个版本chunk&lt;2&gt;,它允许检查r[0]/r[1](带有get&lt;0&gt;(r))。对于性能,我认为编译时间应该更差,但对于运行时,我希望它是相似的(可能范围使代码更难优化)。
  • 我明白你关于符号计数的观点。然而,for 循环与普遍的熟悉程度一致。所需的三个表达式中的每个表达式都是众所周知的,并传达了一个完善的评估顺序:for(scoped init only once; pre loop bool_stop; post_loop actions) 。构造的祝福和诅咒是用户独立控制所有三个阶段。我相信偶然性是造成“原始循环消除”范式的原因。不需要用户循环但允许相同程度的用户错误的解决方案不符合这种观点。
  • @schulmaster 所有的顺序算法应该只推进迭代器。然后,您编写迭代器适配器来执行此类操作。范围提议只是使 输出 序列变得更好(因此它可以作为下一步的输入)。
【解决方案2】:

没有&lt;algorithm&gt; 允许使用非专用迭代器通过非连续输入消耗进行转换。除了专门的迭代器之外,还有第三方,并且(希望)很快成为提供核心 STL 的标准替代方案/增强功能,例如范围(ranges repository)。有关范围的工作示例,请参阅用户 @Jarod42 的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-30
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    相关资源
    最近更新 更多