4

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

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

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

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

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

现在,调用者可以调用 some inserter

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

同样,我们不承诺std::vector专门使用,这很好,因为用户可能只需要 astd::set等中的值。(我应该iterator通过值还是通过引用?)

我的问题是:insert_iterator这样做是正确的还是标准的方法?还是有更好的东西?

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

4

9 回答 9

7

对编辑的回应:好吧,如果您需要返回成百上千的 if 值,那么元组当然不是要走的路。最好选择带有迭代器的解决方案,但最好不要使用任何特定的迭代器类型。


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

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

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

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));
于 2009-02-24T22:26:16.497 回答
6

您可以将智能指针返回到向量。那应该可以工作,并且不会复制向量。

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

于 2009-02-24T22:42:37.597 回答
3

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

于 2009-02-25T02:41:25.027 回答
2

如果您曾经拥有一组对象,那么您可能至少有一些方法可以处理该组对象(否则,您将如何处理它们?)

如果是这种情况,那么将这些方法放在一个同时包含所述对象和方法的类中是有意义的。

如果这是有道理的并且您有这样的课程,请返回它。

我几乎从来没有发现自己认为我希望我可以返回一个以上的值。事实上,一个方法应该只做一件小事,你的参数和返回值往往有关系,所以往往不值得一个包含它们的类,所以返回多个值很少有趣(也许我希望在 20 年内实现 5 次——每次我都进行了重构,都得到了更好的结果,并意识到我的第一次尝试是不合格的。)

于 2009-02-24T22:18:09.107 回答
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();
于 2009-02-24T22:09:57.363 回答
1
  • 一个标准容器适用于同质对象 9 您可以返回)。
  • 标准库的方式是从容器中抽象出一个算法,并使用迭代器来弥补两者之间的差距。
  • 如果您需要传递多个类型,请考虑结构/类。

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

是的。否则,如果您的容器中的元素数量不会与计算值一样多。这并不总是可能的,特别是如果您想写入流。所以,你很好。

于 2009-02-24T22:11:10.480 回答
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);

第一个将使您回到 vector<int> 实现,与您的初始代码相比没有任何明显优势。第二个限制较少,但作为模板实现会给您带来其他限制,这可能使其成为不太理想的选择。

于 2009-02-24T22:30:31.557 回答
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)。

于 2009-02-24T22:54:30.313 回答
0

我想说你的新解决方案更通用,风格更好。我不确定我是否会过多地担心C++ 中的风格,更多地担心可用性和效率。

如果您要返回很多项目,并且知道大小,那么使用向量可以让您在一次分配中保留内存,这可能值得也可能不值得。

于 2009-02-25T13:53:06.520 回答