C++ 返回值的元组与结构
我正试着了解元组(谢谢@litb),对于它们的使用,通常的建议是对于返回大于1值的函数 这是我通常会使用结构的东西,我不理解在这种情况下元组的优点——对于极端懒惰的人来说,这似乎是一种容易出错的方法 ,我会用这个C++ 返回值的元组与结构,c++,tuples,boost-tuples,C++,Tuples,Boost Tuples,我正试着了解元组(谢谢@litb),对于它们的使用,通常的建议是对于返回大于1值的函数 这是我通常会使用结构的东西,我不理解在这种情况下元组的优点——对于极端懒惰的人来说,这似乎是一种容易出错的方法 ,我会用这个 struct divide_result { int quotient; int remainder; }; 使用元组,您将 typedef boost::tuple<int, int> divide_result; …这不会让我充满信心 那么,元组比结
struct divide_result {
int quotient;
int remainder;
};
使用元组,您将
typedef boost::tuple<int, int> divide_result;
…这不会让我充满信心
那么,元组比结构有哪些优点可以弥补歧义呢?对于元组,您可以使用
tie
,这有时非常有用:std::tr1::tie(商,余数)=do_除法()代码>。这对于结构来说并不容易。其次,在使用模板代码时,有时依赖对比为结构类型添加另一个typedef更容易
如果类型不同,那么pair/tuple实际上并不比struct差。例如,考虑pair readFromFile()
,其中int是读取的字节数,bool是eof是否已命中。在这种情况下添加一个结构对我来说似乎太过分了,尤其是这里没有歧义。可以防止代码中充斥着许多结构定义。对于编写代码的人和其他使用它的人来说,只记录元组中的每个元素是什么比编写自己的结构/让人们查找结构定义更容易。元组将更容易编写-无需为每个返回内容的函数创建新的结构。关于去哪里的文档将转到函数文档,这是无论如何都需要的。要使用函数,在任何情况下都需要阅读函数文档,元组将在文档中进行解释。我100%同意你的观点
要从一个方法返回多个值,除了元组之外,您还有几个选项,哪一个选项最好取决于您的情况:
创建一个新的结构。当您返回的多个值是相关的时,这是很好的,创建一个新的抽象是合适的。例如,我认为“divide_result”是一个很好的通用抽象,传递这个实体比传递一个无名的元组更清晰。然后,您可以创建对该新类型进行操作的方法,将其转换为其他数值类型,等等
使用“Out”参数。通过引用传递多个参数,并通过指定给每个out参数来返回多个值。当您的方法返回几个不相关的信息时,这是合适的。在这种情况下,创建一个新的结构将是过分的,如果没有参数,您将强调这一点,并且每个项都会得到它应得的名称
元组是邪恶的。元组在ML或Haskell等语言中非常有用
在C++中,它们的语法使它们不那么优雅,但在以下情况下可能有用:
- 您有一个必须返回多个参数的函数,但结果对于调用者和被调用者都是“本地”的;您不希望仅为此定义结构
- 您可以使用tie函数进行非常有限形式的模式匹配“alaml”,这比出于相同目的使用结构更优雅
- 它们带有预定义的运算符,可以节省时间
元组
我想我同意你的观点,即什么样的立场对应于什么样的变量会引起混乱。但我认为有两个方面。一个是呼叫端,另一个是被叫方:
我认为我们得到了非常清楚的结果,但是如果您必须一次返回更多的值,那么它可能会变得混乱。一旦调用方的程序员查阅了div
的文档,他就会知道位置是什么,并且能够编写有效的代码。根据经验,我建议一次返回的值不要超过4个。对于任何超出此范围的内容,请选择结构
输出参数
当然,也可以使用输出参数:
int remainder;
int quotient;
div(10, 3, "ient, &remainder);
现在我认为这说明了元组比输出参数更好。我们将div
的输入与输出混合,但没有获得任何优势。更糟糕的是,我们让该代码的读者怀疑div
be的实际返回值可能是多少。当输出参数有用时,有一些极好的例子。在我看来,只有在没有其他方法的情况下才应该使用它们,因为返回值已经被接受,不能更改为元组或结构operator>
是使用输出参数的一个很好的例子,因为返回值已经为流保留,所以您可以链接operator>
调用。如果您不需要使用运算符,而且上下文也不是非常清楚,我建议您使用指针,在调用端发出信号,表示对象实际上被用作输出参数,并在适当的情况下添加注释
返回结构
第三个选项是使用结构:
div_result d = div(10, 3);
我认为它肯定会因为清晰而获奖。但是请注意,您仍然需要访问该结构中的结果,并且结果不会“暴露”在表中,因为输出参数和与tie
一起使用的元组就是这样
我认为这些天的一个要点是尽可能使所有的东西都通用。假设你有一个可以打印元组的函数。你可以这么做
cout << div(10, 3);
cout我倾向于将元组与typedef结合使用,以至少部分缓解“无名元组”问题。例如,如果我有一个网格结构,那么:
//row is element 0 column is element 1
typedef boost::tuple<int,int> grid_index;
这是一个有点做作的例子,但我认为大多数时候它在可读性、明确性和易用性之间找到了一个令人满意的中间点
或者在你的例子中:
//quotient is element 0 remainder is element 1
typedef boost:tuple<int,int> div_result;
div_result div(int dividend,int divisor);
//商是元素0,余数是元素1
typedef boost:tuple div_结果
//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);
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);
struct A
{
int a;
int b;
};
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
}
void incrementValues (boost::tuples::null_type) {}
template <typename Tuple_>
void incrementValues (Tuple_ & tuple) {
// ...
++tuple.get_head ();
incrementValues (tuple.get_tail ());
}