【问题标题】:Is std::map + std::tr1::bind + standard algorithms worthwhile?std::map + std::tr1::bind + 标准算法值得吗?
【发布时间】:2008-12-17 21:50:57
【问题描述】:

这是我的question from yesterday 的后续行动。我想到了 Scott Meyers 关于只写代码的警告。我喜欢原则上使用标准算法来访问 std::map 的键或值的想法,但所需的语法有点巴洛克式恕我直言。假设我想将地图的所有键转储到向量。鉴于以下声明,

typedef std::map<int, int> MyMap;
MyMap m;
std::vector<int> v;

哪个代码更易于维护(即,可能更少混淆)?

选项#1:

std::transform(m.begin(),
               m.end(),
               std::back_inserter(v),
               std::tr1::bind(&MyMap::value_type::first, _1));

选项 #2:

for (MyMap::iterator i = m.begin(); i != m.end(); ++i)
{
    v.push_back(i->first);
}

选项 1 是更标准的库式,但我必须在精神上分解它以了解发生了什么。选项 2 似乎更容易阅读,但代价是可能的运行时损失很小。我不会因为 CPU 时间而受到伤害,所以我倾向于选项 2。你们同意吗?我应该考虑第三种选择吗?

附:在写这个问题的过程中,我得出结论,(对于我的项目)读取 std::map 的键的最佳方法是将它们存储在一个侧面容器中并对其进行迭代。可维护性问题仍然存在。

【问题讨论】:

    标签: c++ algorithm stl bind


    【解决方案1】:

    清晰总是胜过聪明。做你以后可以阅读的。

    您并不是唯一一个认为标准代码有点迟钝的人。下一个 C++ 标准将引入 lambda functions,以便您可以使用标准算法编写更清晰的代码。

    【讨论】:

      【解决方案2】:

      第一个和第二个一样可读和可维护——如果你知道bind 做了什么。我一直在使用 Boost::Bind(与 std::tr1::bind 基本相同)足够长的时间,所以我没有遇到任何问题。

      一旦 TR1 成为官方标准的一部分,您可以放心地假设任何称职的 C++ 程序员都会理解它。在那之前,它可能会带来一些困难,但我总是考虑长期而不是短期。

      【讨论】:

      • 我完全同意。很多人担心某些惯用代码的“可读性”,并认为它可能不可读,但当你想到他们的目标受众是什么时,就是那些对你正在编写的语言一无所知的人跨度>
      【解决方案3】:

      你忘了using namespace std::tr1::placeholders :P

      说实话,对于像这样的简单算法,后面的代码可能更容易维护。但我实际上更倾向于前者(尤其是当 C++1x 为我们提供 lambda 时!),因为它强调编程的函数式风格,我个人更喜欢使用循环的命令式风格。

      这真的是一个不同的笔触;标准算法在复杂或通用时最有用,而这两者都不是。

      这是 lambda 的样子:

      std::transform(m.begin(), m.end(), std::back_insterter(v),
                     [](MyMap::value_type pair){ return pair.first; }
                    );
      

      实际上,我更喜欢另一种方法,但它的冗长:

      using std::tr1::bind;
      using std::tr1::placeholders::_1;
      std::for_each(m.begin(), m.end(),
                    bind(&std::vector<int>::push_back, v,
                         bind(&MyMap::value_type::first, _1)
                        )
                   );
      

      还有 lambdas(这可能是所有选项中最简洁和最明确的):

      std::for_each(m.begin(), m.end(),
                    [&v](MyMap::value_type pair){v.push_back(pair.first);}
                   );
      

      【讨论】:

        【解决方案4】:

        我说去 2)

        为了提高性能,您可以让m.end() 退出循环并在向量中保留空间。

        等不及 C++0x 和基于范围的 for 循环;这会让你的循环变得更好。

        【讨论】:

          【解决方案5】:

          选择选项 #1,请参阅 Scott Meyers,Effective STL 第 43 项,第 181 页。

          【讨论】:

          • 但是这个问题的重点是我试图平衡这个概念与第 47 项(避免只写代码)。
          • @Kristo,如果您真的对 STL + TR1 有深入的实践了解,那么选项 #1 对您来说与选项 #2 一样易读。
          【解决方案6】:

          当我昨天查看您的问题时,不是绑定(我经常使用)迫使我看两次以理解代码,而是 map::value_type::first 我没有机会经常使用。虽然我同意“清晰总是胜过聪明”,但在清晰之前需要熟悉,而且你不会熟悉你不使用的样式......

          我还要说,虽然选项 2 在理解预期目的方面更清晰,但它更容易隐藏错误(选项 1 中的任何错误更有可能在编译时可见)。

          【讨论】:

            【解决方案7】:

            我会选择选项 #3:

            #include <boost/range/adaptor/map.hpp>
            #include <boost/range/algorithm_ext/push_back.hpp>
            
            boost::push_back(v, m | boost::adaptors::map_keys);
            

            这样做的好处是:

            1. 更短

            2. 使用命名函数获取密钥

            3. (可能)更高效(因为boost::push_back 可以在v 上调用reserve()

            4. 并且不需要冗余的v.begin()v.end() 对。

            任何其他方式都是纯粹的疯狂。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-08-08
              • 2012-10-12
              • 2010-12-26
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多