在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
是正确或标准的方式吗?还是有更好的东西?
编辑:我编辑了这个问题,明确表示我不是在谈论返回两三个值,而是数百或数千。 (想象一下,您已经返回了在某个目录中找到的所有文件,或图表中的所有边缘等。)
答案 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 ++中的样式,更多关于可用性和效率的内容。
如果您要返回大量项目并知道大小,使用向量将允许您在一次分配中保留内存,这可能是也可能不值得。