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

时间:2021-05-25 02:06:17

In C++, whenever a function creates many (hundreds or thousands of) values, I used to have the caller pass an array that my function then fills with the output values:

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

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

So, the function will fill the vector output with the values it computes. But this is not really good C++ style, as I'm realizing now.

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

The following function signature is better because it doesn't commit to using a std::vector, but could use any container:

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

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

Now, the caller can call with some inserter:

现在,调用者可以使用一些插入器调用:

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

Again, we don't commit to using std::vector specifically, which is nice, because the user might just need the values in a std::set etc. (Should I pass the iterator by value or by reference?)

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

My question is: Is the insert_iterator the right or standard way to do it? Or is there something even better?

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

EDIT: I edited the question to make it clear that I'm not talking about returning two or three values, but rather hundreds or thousands. (Imagine you have return all the files you find in a certain directory, or all the edges in a graph etc.)

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

9 个解决方案

#1


7  

Response to Edit: Well, if you need to return hundreds and thousands if values, a tuple of course would not be the way to go. Best pick the solution with the iterator then, but it's best not use any specific iterator type.

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


If you use iterators, you should use them as generic as possible. In your function you have used an insert iterator like insert_iterator< vector<int> >. You lost any genericity. Do it like this:

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

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

Whatever you give it, it will work now. But it will not work if you have different types in the return set. You can use a tuple then. Also available as std::tuple in the next C++ Standard:

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

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

If the amount of values is variadic and the type of the values is from a fixed set, like (int, bool, char), you can look into a container of boost::variant. This however implies changes only on the call-side. You can keep the iterator style of above:

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

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

#2


6  

You could return a smart pointer to a vector. That should work and no copy of the vector will be made.

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

If you don't want to keep the smart pointer for the rest of your program, you could simply create a vector before calling the function, and swap both vectors.

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

#3


3  

Actually, your old method of passing in the vector has a lot to recommend it -- it's efficient, reliable, and easy to understand. The disadvantages are real but don't apply equally in all cases. Are people really going to want the data in an std::set or list? Are they really going to want to use the long list of numbers without bothering to assign it to a variable first (one of the reasons to return something via 'return' rather than a parameter)? Being generic is nice, but there is a cost in your programming time that may not be redeemed.

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

#4


2  

If you ever have a group of objects, chances are you have at least a few methods that work on that group of objects (otherwise, what are you doing with them?)

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

If that's the case, it would make sense to have those methods in a class that contain both said objects and methods.

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

If that makes sense and you have such a class, return it.

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

I virtually never find myself thinking that I wish I could return more than one value. By the fact that a method should only do one small thing, your parameters and return values tend to have a relationship, and so are more often than not deserving of a class that contains them, so returning more than one value is rarely interesting (Maybe I wished for it 5 times in 20 years--each time I refactored instead, came up with a better result and realized my first attempt was sub-standard.)

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

#5


1  

One other option is boost::tuple: http://www.boost.org/doc/libs/1_38_0/libs/tuple/doc/tuple_users_guide.html

另一个选项是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();

#6


1  

  • A stadard container works for homogenous objects 9which you can return).
  • 标准容器适用于可以返回的同质物体9。

  • The standard library way is to abstract an algorithm from the container and use iterators to bridge the gap between.
  • 标准库方法是从容器中抽象出一个算法,并使用迭代器来弥合它们之间的差距。

  • If you need to pass more than a single type think of structs/classes.
  • 如果你需要传递多个类型的结构/类。

My question is: Is the insert_iterator the right or standard way to do it?

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

Yes. Otherwise, if you are not going to have at least as many elements in your container as there will be computed values. This is not always possible, specially, if you want to write to a stream. So, you are good.

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

#7


1  

Your example with insert_iterator won't work, because insert_iterator is a template requiring a container for a parameter. You could declare it

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

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

or

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

The first will tie you back to a vector<int> implementation, without any obvious advantages over your initial code. The second is less restrictive, but implementing as a template will give you other constraints that might make it a less desirable choice.

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

#8


1  

I'd use something like

我会用类似的东西

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;
}

No copying overhead in the return or risk of leaking (if you use auto_ptr correctly in the caller).

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

#9


0  

I'd say your new solution is more general, and better style. I'm not sure I'd worry too much about style in c++, more about usability and efficiency.

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

If you're returning a lot of items, and know the size, using a vector would allow you to reserve the memory in one allocation, which may or may not be worth it.

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

#1


7  

Response to Edit: Well, if you need to return hundreds and thousands if values, a tuple of course would not be the way to go. Best pick the solution with the iterator then, but it's best not use any specific iterator type.

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


If you use iterators, you should use them as generic as possible. In your function you have used an insert iterator like insert_iterator< vector<int> >. You lost any genericity. Do it like this:

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

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

Whatever you give it, it will work now. But it will not work if you have different types in the return set. You can use a tuple then. Also available as std::tuple in the next C++ Standard:

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

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

If the amount of values is variadic and the type of the values is from a fixed set, like (int, bool, char), you can look into a container of boost::variant. This however implies changes only on the call-side. You can keep the iterator style of above:

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

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

#2


6  

You could return a smart pointer to a vector. That should work and no copy of the vector will be made.

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

If you don't want to keep the smart pointer for the rest of your program, you could simply create a vector before calling the function, and swap both vectors.

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

#3


3  

Actually, your old method of passing in the vector has a lot to recommend it -- it's efficient, reliable, and easy to understand. The disadvantages are real but don't apply equally in all cases. Are people really going to want the data in an std::set or list? Are they really going to want to use the long list of numbers without bothering to assign it to a variable first (one of the reasons to return something via 'return' rather than a parameter)? Being generic is nice, but there is a cost in your programming time that may not be redeemed.

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

#4


2  

If you ever have a group of objects, chances are you have at least a few methods that work on that group of objects (otherwise, what are you doing with them?)

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

If that's the case, it would make sense to have those methods in a class that contain both said objects and methods.

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

If that makes sense and you have such a class, return it.

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

I virtually never find myself thinking that I wish I could return more than one value. By the fact that a method should only do one small thing, your parameters and return values tend to have a relationship, and so are more often than not deserving of a class that contains them, so returning more than one value is rarely interesting (Maybe I wished for it 5 times in 20 years--each time I refactored instead, came up with a better result and realized my first attempt was sub-standard.)

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

#5


1  

One other option is boost::tuple: http://www.boost.org/doc/libs/1_38_0/libs/tuple/doc/tuple_users_guide.html

另一个选项是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();

#6


1  

  • A stadard container works for homogenous objects 9which you can return).
  • 标准容器适用于可以返回的同质物体9。

  • The standard library way is to abstract an algorithm from the container and use iterators to bridge the gap between.
  • 标准库方法是从容器中抽象出一个算法,并使用迭代器来弥合它们之间的差距。

  • If you need to pass more than a single type think of structs/classes.
  • 如果你需要传递多个类型的结构/类。

My question is: Is the insert_iterator the right or standard way to do it?

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

Yes. Otherwise, if you are not going to have at least as many elements in your container as there will be computed values. This is not always possible, specially, if you want to write to a stream. So, you are good.

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

#7


1  

Your example with insert_iterator won't work, because insert_iterator is a template requiring a container for a parameter. You could declare it

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

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

or

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

The first will tie you back to a vector<int> implementation, without any obvious advantages over your initial code. The second is less restrictive, but implementing as a template will give you other constraints that might make it a less desirable choice.

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

#8


1  

I'd use something like

我会用类似的东西

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;
}

No copying overhead in the return or risk of leaking (if you use auto_ptr correctly in the caller).

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

#9


0  

I'd say your new solution is more general, and better style. I'm not sure I'd worry too much about style in c++, more about usability and efficiency.

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

If you're returning a lot of items, and know the size, using a vector would allow you to reserve the memory in one allocation, which may or may not be worth it.

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