如何从C ++函数返回数百个值?

时间:2009-02-24 22:07:01

标签: c++ stl vector iterator return-value

在C ++中,每当函数创建许多(数百或数千)个值时,我曾经让调用者传递一个数组,然后我的函数用输出值填充:

void computeValues(int input, std::vector<int>& output);

因此,该函数将使用它计算的值填充向量output。但这并不是真正优秀的C ++风格,正如我现在意识到的那样。

以下函数签名更好,因为它不承诺使用std::vector,但可以使用任何容器:

void computeValues(int input, std::insert_iterator<int> outputInserter);

现在,来电者可以拨打一些inserter

std::vector<int> values; // or could use deque, list, map, ...
computeValues(input, std::back_inserter(values));

同样,我们不承诺专门使用std::vector,这很好,因为用户可能只需要std::set等中的值(我应该通过iterator按价值或参考?)

我的问题是:insert_iterator是正确或标准的方式吗?还是有更好的东西?

编辑:我编辑了这个问题,明确表示我不是在谈论返回两三个值,而是数百或数千。 (想象一下,您已经返回了在某个目录中找到的所有文件,或图表中的所有边缘等。)

9 个答案:

答案 0 :(得分:7)

对编辑的回应:好吧,如果你需要返回数百和数千的值,那么一个元组当然不是可行的方法。最好用迭代器选择解决方案,但最好不要使用任何特定的迭代器类型。


如果使用迭代器,则应尽可能使用它们。在您的函数中,您使用了insert_iterator< vector<int> >之类的插入迭代器。你失去了任何通用性。这样做:

template<typename OutputIterator>
void computeValues(int input, OutputIterator output) {
    ...
}

无论你给它什么,它现在都会起作用。但如果在返回集中有不同的类型,它将无法工作。你可以使用元组。也可以在下一个C ++标准中以std::tuple形式使用:

boost::tuple<int, bool, char> computeValues(int input) { 
    ....
}

如果值的数量是可变的,并且值的类型来自固定集,如(int,bool,char),则可以查看boost::variant的容器。然而,这仅意味着在呼叫方面的改变。你可以保持上面的迭代器风格:

std::vector< boost::variant<int, bool, char> > data;
computeValues(42, std::back_inserter(data));

答案 1 :(得分:6)

您可以返回一个指向矢量的智能指针。这应该工作,不会制作矢量的副本。

如果你不想为程序的其余部分保留智能指针,你可以在调用函数之前简单地创建一个向量,并交换两个向量。

答案 2 :(得分:3)

实际上,您传递向量的旧方法有很多值得推荐的 - 它高效,可靠且易于理解。缺点是真实的,但并非在所有情况下都同样适用。人们是否真的想要std :: set或list中的数据?他们是否真的想要使用长数字列表而不必先将其分配给变量(通过'return'而不​​是参数返回某些内容的原因之一)?通用性很好,但是编程时间的成本可能无法兑换。

答案 3 :(得分:2)

如果你有一组对象,你可能至少有几种方法可以处理这组对象(否则,你用它们做什么?)

如果是这种情况,那么在包含所述对象和方法的类中使用这些方法是有意义的。

如果这是有道理的并且你有这样的课程,请将其归还。

我几乎从未发现自己在想,我希望自己能够获得多个价值。事实上,一个方法应该只做一件小事,你的参数和返回值往往有一个关系,因此通常不值得包含它们的类,所以返回多个值很少有趣(也许我希望在20年内有5次 - 每次我重构,得出更好的结果,并意识到我的第一次尝试是不合标准的。)

答案 4 :(得分:1)

另一个选项是boost :: tuple:http://www.boost.org/doc/libs/1_38_0/libs/tuple/doc/tuple_users_guide.html

int x, y;
boost::tie(x,y) = bar();

答案 5 :(得分:1)

  • 标准容器适用于可以返回的同质物体。
  • 标准库方法是从容器中抽象算法并使用迭代器来弥合它们之间的差距。
  • 如果你需要传递多个类型的结构/类。
  

我的问题是:insert_iterator是正确的还是标准的方式吗?

是。否则,如果您的容器中至少没有与计算值一样多的元素。这并不总是可行的,特别是如果你想写一个流。所以,你很好。

答案 6 :(得分:1)

使用insert_iterator的示例将不起作用,因为insert_iterator是需要参数容器的模板。你可以声明它

void computeValues(int input, std::insert_iterator<vector<int> > outputInserter);

template<class Container>
void computeValues(int input, std::insert_iterator<Container> outputInserter);

第一个将你带回一个向量&lt; int&gt;实现,没有任何明显优于您的初始代码。第二个限制较少,但作为模板实现会给你其他约束,这可能使它成为一个不太理想的选择。

答案 7 :(得分:1)

我会使用像

这样的东西
std::auto_ptr<std::vector<int> > computeValues(int input);
{
   std::auto_ptr<std::vector<int> > r(new std::vector<int>);
   r->push_back(...) // Hundreds of these
   return r;
}

返回时没有复制开销或泄漏风险(如果在调用者中正确使用auto_ptr)。

答案 8 :(得分:0)

我会说你的新解决方案更通用,风格更好。我不确定我是否也非常担心c ++中的样式,更多关于可用性和效率的内容。

如果您要返回大量项目并知道大小,使用向量将允许您在一次分配中保留内存,这可能是也可能不值得。