代码之家  ›  专栏  ›  技术社区  ›  user1095108

交换值的另一种方式

  •  0
  • user1095108  · 技术社区  · 3 年前

    我想我们都知道如何交换值,但还有另一种方法:

    constexpr auto assign(auto& ...a) noexcept
    {
      return [&](auto const ...v) noexcept { ((a = v), ...); };
    }
    

    要交换,我们需要调用 assign(a, b)(b, a) .

    这是将值交换为基本类型的有效方法吗?它是否比通常的方式提供了更多的优化空间?

    0 回复  |  直到 3 年前
        1
  •  5
  •   Cody Gray    3 年前

    这不是交换值的好方法,即使对于基本类型也是如此。没有标准的“分配成语”;这完全是你编造的。

    即使它有效,也没有理由期望它能产生比 std::swap 哪一个 一个标准的、众所周知的成语。

    我强烈建议您使用 std::交换 相反,这样阅读你的代码的人就会理解,而不必谷歌。

        2
  •  4
  •   Mooing Duck    3 年前

    由于您一直在评论中询问理论:编译器优化的基础是编译器是否能够看到通用模式,从而根据优化度量将其转换为更好的代码。

    为此,编译器可以 真的很聪明 在弄清楚以下是交换时:

    // Conventional (simplified) swap definition
    auto swap(int& a, int& b) -> void {
        const int tmp = a;
        a = b;
        b = tmp;
    }
    

    在您提供的代码中,它应该是 几乎相等 仅适用于基本类型 ,因为它将看到参数 const auto...v 作为 tmp 对象--但是它将看到副本的两组参数。如果我们使的转换变平 assign(a,b)(b,a) ,编译从模板扩展中真正看到的是:

    const auto v1 = a;
    const auto v2 = b;
    b = v1;
    a = v2;
    

    这与标准表达方式相似 swap ,但与编译器经过几十年的优化训练所能识别的内容不太一样。 最有可能 ,编译器会将其视为等效转换,并生成相同的程序集——两者都是如此 gcc and clang (Godbolt链接归功于@HolyBlackCat) .

    请注意,编译器是 真的很聪明 优化以传统/预期方式编写的代码。他们往往不喜欢和挣扎的是试图变得聪明。特别地, 赋值(a,b)(b,a) 要求编译器首先对输入进行扁平化以进行优化,而传统的 swap(a,b) 是为它拼写的。基本上:在 最好的 你会得到同样的事情只是传统的方式。

    为此,这不是 好的 交换值的方式 。很可能是这样 提供更好的优化(如果有的话,可能会稍微差一点)。

    如果我们将这个定义扩展到包括泛型,情况会变得更糟,因为 const auto 提供了更多的副本,并且不执行正确的移动(正确的移动语义也有助于编译器)。此外,它在语义上不读作“ 交换 鉴于 掉期(a,b) 甚至 std::tie(a,b) = std::make_tuple(b,a) 不那么模棱两可。


    同样,这也不是一个“成语” 从不 通过使用被确立为已经是一种模式。

        3
  •  0
  •   Cody Gray    3 年前

    我认为,如果你同时分配多个值,其中一些受让人(不确定这是什么术语?)可能会在这个过程中发生变化,这是有道理的。

    例如,进行旋转:

    int a = 10, b = 20, c = 30;
    a = b;
    b = c;
    c = a; // oops you might wanted c = 10, but c is actually 20 now.
    

    但是,如果仅交换2个值,则 std::swap 在可读性和可优化性方面,可能是更好的方法。

    虽然可读性是主观的,至少对我来说, swap(a, b) 更容易理解,而 assign(a, b)(b, a) 并不意味着直接交换的想法。

    可优化性需要以基准为准才能确定,但据我所知, std::交换 针对基本类型进行了很好的优化,我不认为你的方法会进一步优化它。然而,如果没有实际的基准测试,我无法给你一个确定的答案。

    推荐文章