я думаю, что соглашаюсь с Вами, что проблема с тем, какое положение соответствует тому, какая переменная может представить беспорядок. Но я думаю, что существует две стороны. Каждый , сторона вызова и другой сторона вызываемого :
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);
я думаю, что определенно получает премию за [1 125] четкость . Но обратите внимание, что необходимо все еще получить доступ к результату в той структуре, и результат не "раскрыт" на таблице, поскольку это имело место для выходных параметров и кортежа, используемого с tie
.
я думаю, что важный пункт в эти дни должен сделать все максимально универсальным. Так, скажите, что у Вас есть функция, которая может распечатать кортежи. Можно просто сделать
cout << div(10, 3);
И отображать результат. Я думаю, что кортежи, с другой стороны, ясно побеждают для их универсальный природа. Делая это с div_result, необходимо перегрузить operator< < или потребность произвести каждого участника отдельно.
Другая опция состоит в том, чтобы использовать карту 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);
также существуют другие преимущества, такие как способность к выполните итерации по полям карты, и т.д. и т.д. Посмотрите doco для получения дополнительной информации.
С кортежами можно использовать tie
, который иногда довольно полезен: std::tr1::tie (quotient, remainder) = do_division ();
. Это не настолько легко со структурами. Во-вторых, при использовании шаблона кода, иногда легче полагаться на пар, чем добавить еще одно определение типа для типа структуры.
И если типы отличаются, то пара/кортеж действительно не хуже, чем структура. Думайте, например pair<int, bool> readFromFile()
, где интервал является количеством чтения байтов, и bool - был ли eof поражен. Добавление структуры в этом случае походит на излишество для меня, тем более, что нет никакой неоднозначности здесь.
Кортежи очень полезны на языках, таких как ML или Haskell.
В C++, их синтаксис делает их менее изящными, но может быть полезным в следующих ситуациях:
у Вас есть функция, которая должна возвратить больше чем один аргумент, но результат "локален" для вызывающей стороны и вызываемого; Вы не хотите определять структуру только для этого
, можно использовать функцию связи, чтобы сделать очень ограниченную форму сопоставления с образцом "а-ля ML", который более изящен, чем использование структуры для той же цели.
они идут с предопределенным < операторы, которые могут сэкономить время.
Я склонен использовать кортежи в сочетании с определениями типов к, по крайней мере, частично облегчают 'неназванный кортеж' проблема. Например, если у меня была структура сетки тогда:
//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);
Предотвращает Ваш код, замусоренный многими определениями структуры. Для человека, пишущего код, и для другого использования его просто документ, каков каждый элемент в кортеже, вместо того, чтобы писать Вашим собственным людям структуры/создания, при поиске определения структуры легче.
Кортежи будет легче записать - никакая потребность создать новую структуру для каждой функции, которая возвращает что-то. Документация о том, что идет, куда перейдет к функциональной документации, которая будет необходима так или иначе. Для использования функции, нужно будет прочитать функциональную документацию в любом случае, и кортеж будет объяснен там.
Я согласовываю с Вами 100% Roddy.
Для возврата нескольких значений из метода у Вас есть несколько опций кроме кортежей, какой является лучшим, зависит от Вашего случая:
Создание новой структуры. Это хорошо, когда несколько значений, которые Вы возвращаете, , имел отношение , и уместно создать новую абстракцию. Например, я думаю, что "divide_result" является хорошей общей абстракцией, и раздающий этот объект делает Ваш код намного более ясным, чем просто раздавание неназванного кортежа. Вы могли тогда создать методы, которые воздействуют на этот новый тип, преобразовывают его в другие числовые типы, и т.д.
Используя параметры. Передайте несколько параметров ссылкой и возвратите несколько значений путем присвоения каждому параметра. Это является соответствующим, когда Ваш метод возвращает несколько не связанный сведения. Создание новой структуры в этом случае было бы излишеством, и с параметрами Вы подчеркиваете эту мысль, плюс каждый объект завоевывает репутацию, это заслуживает.
Кортежи являются Злыми.
Одной из особенностей кортежей, которых нет в структурах, является их инициализация. Рассмотрим что-то вроде следующего:
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
}
Компилятор не может инициализировать "Tuple" с результатом make_tuple
, и таким образом генерирует ошибку, которая позволяет вам указать правильные значения для третьего параметра.
Наконец, другое преимущество кортежей состоит в том, что они позволяют вам писать код, который выполняет итерацию по каждому значению. Это просто невозможно с использованием структуры.
void incrementValues (boost::tuples::null_type) {}
template <typename Tuple_>
void incrementValues (Tuple_ & tuple) {
// ...
++tuple.get_head ();
incrementValues (tuple.get_tail ());
}