【问题标题】:Is wrapping STL idioms for readability a good idea?包装 STL 习语以提高可读性是个好主意吗?
【发布时间】:2010-07-29 17:27:15
【问题描述】:

我目前正在开发一个需要尽可能少的外部依赖项的 C++ 项目,因此我非常坚持使用 STL 和 Boost。直到现在,当谈到 C++ 时,我几乎只生活在 Qt 领域。一般来说,我倾向于尽可能使用 C# 和 Python。

今天我想检查std::vector 是否包含某个项目。使用 Qt,我会这样做:

QList< int > list;
list.append( 1 );
list.append( 2 );
list.append( 3 );

if ( list.contains( 2 ) )
{
    // do something
}

美观且易读。但是std::vector 没有contains 方法,这是一个惊喜。好的......这样的STL成语会是什么?搜了一圈,好像是这样的:

std::vector< int > list;
list.push_back( 1 );
list.push_back( 2 );
list.push_back( 3 );

std::vector< int >::const_iterator result =
    std::find( list.begin(), list.end(), 2 );

if ( result != list.end() )
{
    // do something
}

那(对我来说)很难读,而且过于冗长。所以我发现自己编写了一个实用函数,它接受一个向量和一个值,并根据是否找到该值返回bool。基本上,一个模板化的contains() 方法;上述std::find 调用的包装器。然后我可以以类似于 Qt 示例的方式使用它。

我想到了几个类似的实用程序函数,它们将包装其他 STL 习语,没有其他原因,只是为了提高(感知)可读性。我想知道的是……这是个坏主意吗?其他人也这样做吗?我错过了一些重要的东西吗?代码在某一时刻将是 OSS,我宁愿不做一些其他 C++ 开发人员会觉得奇怪的特殊事情。

【问题讨论】:

  • @rgrig 这对我来说仍然不如 Qt 示例可读,而且正如你所说,它不是很有效。它应该是 O(n/2),而不是 O(n)。
  • 我不确定您指的是什么,因为它显然已被删除,但 O(n/2) 是 O(n)。但是,在效率方面,可以为集合/映射重载这样的包装器,以利用它们进行排序,而无需在调用站点进行任何更改。

标签: c++ stl readability


【解决方案1】:

boost 让它更整洁。我从不使用 STL 不再基于迭代器的算法。范围 基于算法是一个更整洁的抽象 并产生更简洁的代码。

#include <boost/range/algorithm/find.hpp>

void foo(){
    std::vector<int> list;
    ...
    ...
    boost::find(list, 2) != list.end()
}

【讨论】:

    【解决方案2】:

    编写有助于您并使您的代码更简洁的实用程序函数并没有错。其他人也这样做。 Boost library 是此类实用函数和类的最大集合。

    多说 C++ 标准明确提议扩展标准库 (17.3.1.2/1):

    该库可以通过 C++ 程序进行扩展。如果适用,每个条款都描述了此类扩展必须满足的要求。此类扩展通常是以下之一:

    • 模板参数
    • 派生类
    • 满足接口约定的容器、迭代器和/或算法

    【讨论】:

      【解决方案3】:

      我会说这绝对是个好主意。 C++ STL 缺少很多 Python/C# 程序员对标准库的期望。如果您可以通过采用 2-3 行 STL 方法并使其成为单个函数来使您的代码更具可读性,那就继续吧!

      这是另一个非常相似的问题示例:我经常想将 int 转换为 std::string。令我惊讶的是,使用 STL 并没有简洁的方法来做到这一点。因此,我编写了一个 toStr 函数,它运行了将 int 放入 stringstream 并返回结果 string 所需的 2-3 行。

      编辑:为了澄清,我建议在创建自己的解决方案之前寻找boost 解决方案。我的示例旨在展示 STL 的局限性,但有另一种解释:“无论 STL 缺少什么,boost 都有。”

      【讨论】:

      • boost 具有 lexical_cast() 可以将任何可流式传输的内容转换为字符串。
      • boost::lexical_cast(str) 也将执行相反的操作并解析字符串并返回一个 int。
      • 确实,这就是我现在使用的。我只是举个例子。 :)
      • C++0x 有std::to_string() 可以将任何数字类型转换为字符串。 g++ 支持它(以及std::stoi()std::stol() 等,反之亦然)
      • @Cubbi:现在我不知道。谢谢!
      【解决方案4】:

      类似的,在我当前的项目中,我们有一个文件叫做:stlutils.h,里面包含了一些方法,比如contains()。实现为:

      template<class Container, class T>
      bool contains(const Container& c, const T& value) {
         return std::find(c.begin(), c.end(), value) != c.end();
      }
      

      还有更多的功能,但我想你明白了

      【讨论】:

        【解决方案5】:

        其他答案表明您可以编写实用程序函数来为您执行此操作,这是您需要它的好主意。但我想我要指出一个重点:STL 是围绕算法效率设计的。几乎所有使用 STL 的操作都有标准规定的 big-O 效率要求。

        如果vector 有一个contains() 成员,调用肯定是O(n),因为vector 是一个简单的连续列表。由于它也很方便,它可能会鼓励程序员定期使用它,即使是在大型数据集上,也会鼓励设计算法性能较差的应用程序。在contains() 的情况下,如果查找容器是否包含某个元素很重要,同时保证所有元素都是唯一的,std::set 几乎肯定是一个更好的选择,效率为 O(log n) std::unordered_set 的查找,甚至 O(1)。

        所以我的个人观点是:学习 STL 提供的所有容器和功能,你会发现虽然它很简洁,但它鼓励了一种更高效的编程风格。您在问题中询问您是否缺少某些东西,我会说是的 - 您想更仔细地考虑您使用的容器。这些天我经常使用set 而不是vector

        【讨论】:

          【解决方案6】:

          我不是 wrappers 的忠实粉丝,但如果他们能帮到你,那就去做吧。我认为随着时间的推移,您会发​​现您希望将实用程序函数与 std::vector 之外的其他容器一起使用。最终,您的实用程序函数变得如此通用,以至于您不妨直接使用 std::find。

          但是您确定您使用的是正确的容器吗? std::set 有一个方法 count(),它本质上等同于 contains()。它是 O(log(n)),而不是 O(n)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-03
            • 2016-08-18
            相关资源
            最近更新 更多