返回值的 Boost::Tuples 与 Structs

发布于 2024-07-11 08:02:45 字数 674 浏览 16 评论 0原文

我正在尝试了解元组(感谢@litb),使用它们的常见建议是返回 > 的函数。 1 值。

这是我通常使用结构体的东西,在这种情况下我无法理解元组的优点——对于极度懒惰的人来说,这似乎是一种容易出错的方法。

借用一个例子,我会使用这个

struct divide_result {
    int quotient;
    int remainder;
};

使用元组,你会但是

typedef boost::tuple<int, int> divide_result;

如果不阅读您正在调用的函数的代码(或者注释,如果您愚蠢到相信它们),您将不知道哪个 int 是商,反之亦然。 看起来很像……

struct divide_result {
    int results[2]; // 0 is quotient, 1 is remainder, I think
};

这不会让我充满信心。

那么,相比于结构体,元组有哪些优势可以弥补歧义呢?

I'm trying to get my head around tuples (thanks @litb), and the common suggestion for their use is for functions returning > 1 value.

This is something that I'd normally use a struct for , and I can't understand the advantages to tuples in this case - it seems an error-prone approach for the terminally lazy.

Borrowing an example, I'd use this

struct divide_result {
    int quotient;
    int remainder;
};

Using a tuple, you'd have

typedef boost::tuple<int, int> divide_result;

But without reading the code of the function you're calling (or the comments, if you're dumb enough to trust them) you have no idea which int is quotient and vice-versa. It seems rather like...

struct divide_result {
    int results[2]; // 0 is quotient, 1 is remainder, I think
};

...which wouldn't fill me with confidence.

So, what are the advantages of tuples over structs that compensate for the ambiguity?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

嗫嚅 2024-07-18 08:02:45

元组

我想我同意你的观点,即什么位置对应于什么变量的问题可能会引起混乱。 但我认为有两个方面。 一个是调用方,另一个是被调用方

int remainder; 
int quotient;
tie(quotient, remainder) = div(10, 3);

我认为我们得到的内容非常清楚,但如果您必须返回更多值,它可能会变得混乱立刻。 一旦调用者的程序员查阅了div的文档,他就会知道什么位置是什么,并且可以编写有效的代码。 根据经验,我会说不要一次返回超过 4 个值。 对于除此之外的任何事情,更喜欢结构。

输出参数

当然,也可以使用输出参数:

int remainder; 
int quotient;
div(10, 3, "ient, &remainder);

现在我认为这说明了元组如何比输出参数更好。 我们将 div 的输入与其输出混合在一起,但没有获得任何优势。 更糟糕的是,我们让该代码的读者对 div实际返回值产生疑问。 有输出参数有用的精彩示例。 在我看来,只有在别无选择的情况下才应该使用它们,因为返回值已被采用并且无法更改为元组或结构。 operator>> 是使用输出参数的一个很好的示例,因为返回值已为流保留,因此您可以链接 operator>> 调用。 如果您不涉及运算符,并且上下文不是很清楚,我建议您使用指针,在调用方发出信号,表明该对象实际上用作输出参数,此外还添加了适当的注释。

返回结构

第三种选择是使用结构:

div_result d = div(10, 3);

我认为这绝对赢得了清晰度奖。 但请注意,您仍然必须访问该结构中的结果,并且结果不会“裸露”在表上,就像输出参数和与 tie 一起使用的元组的情况一样。

我认为现在的一个要点是让一切尽可能通用。 因此,假设您有一个可以打印元组的函数。 你可以直接做

cout << div(10, 3);

并显示你的结果。 我认为,另一方面,元组显然因其多功能性质而获胜。 使用 div_result 执行此操作,您需要重载运算符<<,或者需要单独输出每个成员。

tuples

I think i agree with you that the issue with what position corresponds to what variable can introduce confusion. But i think there are two sides. One is the call-side and the other is the callee-side:

int remainder; 
int quotient;
tie(quotient, remainder) = div(10, 3);

I think it's crystal clear what we got, but it can become confusing if you have to return more values at once. Once the caller's programmer has looked up the documentation of div, he will know what position is what, and can write effective code. As a rule of thumb, i would say not to return more than 4 values at once. For anything beyond, prefer a struct.

output parameters

Output parameters can be used too, of course:

int remainder; 
int quotient;
div(10, 3, "ient, &remainder);

Now i think that illustrates how tuples are better than output parameters. We have mixed the input of div with its output, while not gaining any advantage. Worse, we leave the reader of that code in doubt on what could be the actual return value of div be. There are wonderful examples when output parameters are useful. In my opinion, you should use them only when you've got no other way, because the return value is already taken and can't be changed to either a tuple or struct. operator>> is a good example on where you use output parameters, because the return value is already reserved for the stream, so you can chain operator>> calls. If you've not to do with operators, and the context is not crystal clear, i recommend you to use pointers, to signal at the call side that the object is actually used as an output parameter, in addition to comments where appropriate.

returning a struct

The third option is to use a struct:

div_result d = div(10, 3);

I think that definitely wins the award for clearness. But note you have still to access the result within that struct, and the result is not "laid bare" on the table, as it was the case for the output parameters and the tuple used with tie.

I think a major point these days is to make everything as generic as possible. So, say you have got a function that can print out tuples. You can just do

cout << div(10, 3);

And have your result displayed. I think that tuples, on the other side, clearly win for their versatile nature. Doing that with div_result, you need to overload operator<<, or need to output each member separately.

半﹌身腐败 2024-07-18 08:02:45

另一种选择是使用 Boost Fusion 地图(代码未经测试):

struct quotient;
struct remainder;

using boost::fusion::map;
using boost::fusion::pair;

typedef map<
    pair< quotient, int >,
    pair< remainder, int >
> div_result;

您可以相对直观地访问结果:

using boost::fusion::at_key;

res = div(x, y);
int q = at_key<quotient>(res);
int r = at_key<remainder>(res);

还有其他优点,例如能够迭代地图的字段等有关详细信息,请参阅doc

Another option is to use a Boost Fusion map (code untested):

struct quotient;
struct remainder;

using boost::fusion::map;
using boost::fusion::pair;

typedef map<
    pair< quotient, int >,
    pair< remainder, int >
> div_result;

You can access the results relatively intuitively:

using boost::fusion::at_key;

res = div(x, y);
int q = at_key<quotient>(res);
int r = at_key<remainder>(res);

There are other advantages too, such as the ability to iterate over the fields of the map, etc etc. See the doco for more information.

私藏温柔 2024-07-18 08:02:45

对于元组,您可以使用tie,这有时非常有用:std::tr1::tie (quotient, approximation) = do_division ();。 对于结构来说这并不容易。 其次,使用模板代码时,有时依赖对比为结构类型添加另一个 typedef 更容易。

如果类型不同,那么对/元组实际上并不比结构差。 例如,pairreadFromFile(),其中int是读取的字节数,bool是是否到达eof。 在这种情况下添加结构对我来说似乎有点过分了,特别是因为这里没有歧义。

With tuples, you can use tie, which is sometimes quite useful: std::tr1::tie (quotient, remainder) = do_division ();. This is not so easy with structs. Second, when using template code, it's sometimes easier to rely on pairs than to add yet another typedef for the struct type.

And if the types are different, then a pair/tuple is really no worse than a struct. Think for example pair<int, bool> readFromFile(), where the int is the number of bytes read and bool is whether the eof has been hit. Adding a struct in this case seems like overkill for me, especially as there is no ambiguity here.

你曾走过我的故事 2024-07-18 08:02:45

元组在 ML 或 Haskell 等语言中非常有用。

在 C++ 中,它们的语法使其不太优雅,但在以下情况下可能很有用:

  • 您有一个必须返回多个参数的函数,但结果对于调用者和被调用者来说是“本地”的; 您不想为此定义一个结构

  • 您可以使用 tie 函数来执行非常有限的模式匹配“a la ML”形式,这比使用结构来实现相同目的更优雅。< /p>

  • 它们带有预定义的< 运算符,这可以节省时间。

Tuples are very useful in languages such as ML or Haskell.

In C++, their syntax makes them less elegant, but can be useful in the following situations:

  • you have a function that must return more than one argument, but the result is "local" to the caller and the callee; you don't want to define a structure just for this

  • you can use the tie function to do a very limited form of pattern matching "a la ML", which is more elegant than using a structure for the same purpose.

  • they come with predefined < operators, which can be a time saver.

风透绣罗衣 2024-07-18 08:02:45

我倾向于将元组与 typedef 结合使用,以至少部分缓解“无名元组”问题。 例如,如果我有一个网格结构,那么:

//row is element 0 column is element 1
typedef boost::tuple<int,int> grid_index;

然后我使用命名类型为:

grid_index find(const grid& g, int value);

这是一个有点人为的示例,但我认为大多数时候它在可读性、明确性和易用性之间达到了一个令人满意的平衡点。

或者在你的例子中:

//quotient is element 0 remainder is element 1
typedef boost:tuple<int,int> div_result;
div_result div(int dividend,int divisor);

I tend to use tuples in conjunction with typedefs to at least partially alleviate the 'nameless tuple' problem. For instance if I had a grid structure then:

//row is element 0 column is element 1
typedef boost::tuple<int,int> grid_index;

Then I use the named type as :

grid_index find(const grid& g, int value);

This is a somewhat contrived example but I think most of the time it hits a happy medium between readability, explicitness, and ease of use.

Or in your example:

//quotient is element 0 remainder is element 1
typedef boost:tuple<int,int> div_result;
div_result div(int dividend,int divisor);
仅此而已 2024-07-18 08:02:45

元组的一项功能是结构所没有的,那就是它们的初始化。 考虑如下情况:

struct A
{
  int a;
  int b;
};

除非您编写一个 make_tuple 等效项或构造函数,否则要使用此结构作为输入参数,您首先必须创建一个临时对象:

void foo (A const & a)
{
  // ...
}

void bar ()
{
   A dummy = { 1, 2 };
   foo (dummy);
}

不过,还不错,以维护的情况为例无论出于何种原因,都会向我们的结构添加一个新成员:

struct A
{
  int a;
  int b;
  int c;
};

聚合初始化的规则实际上意味着我们的代码将继续编译而无需更改。 因此,我们必须搜索该结构的所有用法并更新它们,而无需编译器的任何帮助。

将此与元组进行对比:

typedef boost::tuple<int, int, int> Tuple;
enum {
  A
  , B
  , C
};

void foo (Tuple const & p) {
}

void bar ()
{
  foo (boost::make_tuple (1, 2));  // Compile error
}

编译器无法使用 make_tuple 的结果初始化“Tuple”,因此会生成错误,允许您为第三个参数指定正确的值。

最后,元组的另一个优点是它们允许您编写迭代每个值的代码。 使用结构体根本不可能实现这一点。

void incrementValues (boost::tuples::null_type) {}

template <typename Tuple_>
void incrementValues (Tuple_ & tuple) {
   // ...
   ++tuple.get_head ();
   incrementValues (tuple.get_tail ());
}

One feature of tuples that you don't have with structs is in their initialization. Consider something like the following:

struct A
{
  int a;
  int b;
};

Unless you write a make_tuple equivalent or constructor then to use this structure as an input parameter you first have to create a temporary object:

void foo (A const & a)
{
  // ...
}

void bar ()
{
   A dummy = { 1, 2 };
   foo (dummy);
}

Not too bad, however, take the case where maintenance adds a new member to our struct for whatever reason:

struct A
{
  int a;
  int b;
  int c;
};

The rules of aggregate initialization actually mean that our code will continue to compile without change. We therefore have to search for all usages of this struct and updating them, without any help from the compiler.

Contrast this with a tuple:

typedef boost::tuple<int, int, int> Tuple;
enum {
  A
  , B
  , C
};

void foo (Tuple const & p) {
}

void bar ()
{
  foo (boost::make_tuple (1, 2));  // Compile error
}

The compiler cannot initailize "Tuple" with the result of make_tuple, and so generates the error that allows you to specify the correct values for the third parameter.

Finally, the other advantage of tuples is that they allow you to write code which iterates over each value. This is simply not possible using a struct.

void incrementValues (boost::tuples::null_type) {}

template <typename Tuple_>
void incrementValues (Tuple_ & tuple) {
   // ...
   ++tuple.get_head ();
   incrementValues (tuple.get_tail ());
}
笔芯 2024-07-18 08:02:45

防止您的代码中充斥着许多结构定义。 当您只记录元组中的每个元素是什么,而不是编写自己的结构/让人们查找结构定义时,对于编写代码的人和其他使用它的人来说会更容易。

Prevents your code being littered with many struct definitions. It's easier for the person writing the code, and for other using it when you just document what each element in the tuple is, rather than writing your own struct/making people look up the struct definition.

°如果伤别离去 2024-07-18 08:02:45

元组将更容易编写 - 无需为每个返回内容的函数创建一个新的结构。 关于什么去哪里的文档将转到函数文档,无论如何都需要函数文档。 要使用该函数,在任何情况下都需要阅读函数文档,其中将解释元组。

Tuples will be easier to write - no need to create a new struct for every function that returns something. Documentation about what goes where will go to the function documentation, which will be needed anyway. To use the function one will need to read the function documentation in any case and the tuple will be explained there.

烟酉 2024-07-18 08:02:45

我百分百同意你的观点,罗迪。

要从方法返回多个值,除了元组之外,您还有多种选择,哪一个最好取决于您的情况:

  1. 创建一个新结构。 当您返回的多个值相关时,这很好,并且适合创建新的抽象。 例如,我认为“divide_result”是一个很好的通用抽象,并且传递这个实体使您的代码比仅仅传递无名元组更清晰。 然后,您可以创建对该新类型进行操作的方法,将其转换为其他数字类型等。

  2. 使用“Out”参数。 通过引用传递多个参数,并通过分配给每个输出参数来返回多个值。 当您的方法返回多个不相关信息时,这是合适的。 在这种情况下创建一个新的结构将是多余的,并且使用 Out 参数可以强调这一点,而且每个项目都获得它应有的名称。

元组是邪恶的。

I agree with you 100% Roddy.

To return multiple values from a method, you have several options other than tuples, which one is best depends on your case:

  1. Creating a new struct. This is good when the multiple values you're returning are related, and it's appropriate to create a new abstraction. For example, I think "divide_result" is a good general abstraction, and passing this entity around makes your code much clearer than just passing a nameless tuple around. You could then create methods that operate on the this new type, convert it to other numeric types, etc.

  2. Using "Out" parameters. Pass several parameters by reference, and return multiple values by assigning to the each out parameter. This is appropriate when your method returns several unrelated pieces of information. Creating a new struct in this case would be overkill, and with Out parameters you emphasize this point, plus each item gets the name it deserves.

Tuples are Evil.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文