C++ STL算法:为什么没有额外的容器接口(除了迭代器对)?

C++ STL算法:为什么没有额外的容器接口(除了迭代器对)?,c++,stl,c++11,overloading,stl-algorithm,C++,Stl,C++11,Overloading,Stl Algorithm,我想知道为什么STL没有重载它们的算法函数,这样我就可以通过简单地提供一个容器来调用它们,而不是采用更详细的方式来传递begin+end迭代器。我当然理解为什么我们还希望使用迭代器对来处理容器/数组的子序列,但是,几乎所有对这些方法的调用都使用整个容器: std::for_each(myVector.begin(), myVector.end(), doSomething); 我会发现它更方便,可读性和可维护性只是写 std::for_each(myVector, doSomething);

我想知道为什么STL没有重载它们的算法函数,这样我就可以通过简单地提供一个容器来调用它们,而不是采用更详细的方式来传递begin+end迭代器。我当然理解为什么我们还希望使用迭代器对来处理容器/数组的子序列,但是,几乎所有对这些方法的调用都使用整个容器:

std::for_each(myVector.begin(), myVector.end(), doSomething);
我会发现它更方便,可读性和可维护性只是写

std::for_each(myVector, doSomething);
STL没有提供这些重载的原因是什么?[编辑:我不是要用这个受限的接口替换接口,而是要提供一个基于容器的iterface!]它们会引入歧义吗?我在想这样的事情:

template<typename _Container, typename _Funct>
inline _Funct for_each(_Container c, _Funct f) {
    return for_each(begin(c), end(c), f);
}
auto newVector = myVector * doSomething;
模板
每个函数的内联函数(\u容器c,\u函数f){
返回每个_(开始(c)、结束(c)、f);
}

我遗漏了什么吗?

不幸的是,这是一个更普遍的问题;也就是说,迭代器的设计是为了击败那些蹩脚的C API和Java风格的“将算法作为每个容器的方法”解决方案。它们是第一代通用解决方案,经过反思,它们不如我们花了二十年时间思考后得到的其他可能的通用解决方案好,这一点也不奇怪


添加这些容器重载只会在问题空间的一小部分上起到辅助作用;这甚至可能让事情在未来变得更糟。解决方案是C++,希望能尽快引入。

理解我认为必须理解C++算法的哲学。让我们先问这个问题:

< C++ >为什么C++算法被实现为自由函数而不是成员函数?< /强>

答案非常简单:避免实现爆炸。假设您有
M
容器和
N
算法,如果您将它们实现为容器的成员,那么将有
M*N
实现。这种方法存在两个(相关)问题:

  • 首先,它没有利用代码重用。大多数实现都将重复
  • 第二,实施爆炸,这是上述情况的直接后果
C++通过将它们实现为自由函数来解决这些问题,因此您只有
N
实现。在容器上运行的每个算法都使用一对迭代器,用于定义范围。如果你想要装载容器的重载,而不是一对迭代器,那么标准必须为每个算法提供这样的重载,并且将有<代码> 2 *N/COD>实现,这几乎完全违背了C++最初将算法从容器中分离的目的。这些函数中的一半不做其他一半做不到的事情

所以我认为这不是什么大问题。为了避免一个单参数,为什么要实现多个函数(这也会对其使用施加一些限制,例如不能向其传递指针)?然而,如果程序员想要在他们的实用程序中实现这些函数,他们可以随时根据标准算法与其他许多函数一起实现它们


你评论道:

实际上,2*N个实现只有N个实现。其他N个是内联重载,它们直接调用算法的“真实”版本,因此它们是唯一的头。提供容器重载并不会破坏将算法与容器分离的目的,因为(正如您在我的示例中看到的)它们可以使用模板来处理所有类型的容器


基于这种逻辑,我们可以很好地论证
M*N
算法。那么,让它们也成为成员函数(并在内部调用自由函数)?我相信很多OOP的人都会喜欢

auto result = container.accumulate(val);
结束

有一个解决这个问题的计划。 长篇大论被删去了好几次

您的示例如下所示:

template<typename _Container, typename _Funct>
inline _Funct for_each(_Container c, _Funct f) {
    return for_each(begin(c), end(c), f);
}
auto newVector = myVector * doSomething;
是,
doSomething
-没有括号

来自shell的熟悉习惯用法(使用std算法):

auto t=vector{3,2,1,4}排序|唯一;

它们确实为许多算法引入了模糊性。大量的

以下是赫伯·萨特博客的相关答案:。它显示了反例,就像Bo Persson在上面的回答中所做的那样。

应该指出的是,定义自己的琐碎包装来添加容器化版本非常容易

例如:

template<typename Container, typename Func>
Func for_each(Container& c, Func f) {
    return std::for_each(c.begin(), c.end(), f);
}
模板
每个函数(容器和c,函数f){
返回std::for_each(c.begin()、c.end()、f);
}

现在你可以打你想要的简单电话了。没有歧义,因为包装器不在std名称空间中。您可以定义使用const Container&的重载。如果您想要调用C++-11常量迭代器方法的版本(例如cbegin()),我认为您需要以不同的方式命名包装器。我使用for each_const.

显然,正如其他用户所提到的,这是一个棘手的问题,所以不幸的是,这已经很长时间了,在标准库中仍然没有解决方案。但是,已经有一些可用的范围库,例如Boost::range和Adobe源代码库中的一个,它们不仅提供了您在问题中描述的界面的简单性,而且还提供了一些更高级的功能

您的示例与Boost完美结合(我们正在
使用名称空间Boost::range::Adapters
如下):

boost::用于每个(myVector,doSomething)

我们还可以快速方便地切片
myVector

boost::对于每个(myVector)|切片(10,20),doSomething)

我们甚至可以使用另一个压缩
myVector
,通过谓词进行过滤,并在一个简单的语句中对结果对的每个其他元素进行采样(
template<class container, class funct>
void do_something(container, funct);
#include <iostream>
#include <vector>

template<class iterator>
void test(iterator, iterator)
{
   std::cout << "test iterator\n";
}

template<class iterator, class predicate>
void test(iterator, iterator, predicate)
{
   std::cout << "test iterator, predicate\n";
}

template<class container, class predicate>
void test(const container& cont, predicate compare)
{
   std::cout << "test container, predicate\n";

   test(cont.begin(), cont.end(), compare);
}

template<class container>
class adapter
{
public:
   typedef typename container::iterator   iterator;

   adapter(container* cont) : cont(cont)
   { }

   iterator begin() const
   { return cont->begin(); }

   iterator end() const
   { return cont->end(); }

   bool operator()(const iterator& one, const iterator& two)
   { return *one < *two; }

private:
   container* cont;
};

int main()
{
   std::vector<int>   v;

   adapter<std::vector<int>>   a(&v);

   test(a, a);

}
template<typename Container, typename Func>
Func for_each(Container& c, Func f) {
    return std::for_each(c.begin(), c.end(), f);
}