7

这是我昨天提出的问题的后续。我想到了 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。你们同意吗?我应该考虑第三种选择吗?

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

4

7 回答 7

11

清晰总是胜过聪明。做你以后能读到的。

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

于 2008-12-17T22:18:09.987 回答
5

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

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

于 2008-12-17T22:18:46.537 回答
4

你忘了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);}
             );
于 2008-12-17T22:24:11.377 回答
3

我说去2)

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

等不及 C++0x 和基于范围的 for 循环了;这将使您的循环更好。

于 2008-12-17T21:58:21.273 回答
1

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

于 2008-12-18T00:55:24.537 回答
1

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

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

于 2008-12-18T15:39:36.343 回答
1

我会选择选项#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可以调用reserve()v

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

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

于 2012-03-23T23:28:14.020 回答