Pull to refresh

Comments 96

Я не очень силен в C++, а если попробовать заменить Ваше:

struct Person
{
string Name;
string Surname;
}
...
Person GetInfo( int personId );


на

typedef pair<string, string> Person;
...
Person GetInfo( int personId );
и можно еще так сказать дальше пойти
typedef string Name;
typedef string Surname;
typedef pair<Name, Surname> Person;
...
Person GetInfo( int personId );


или это плохо будет?
Лучше. Но я бы поддержал автора — в данном случае лучше классы/структуры. Даже представить не могу, что кому-то придет в голову описывать данные таким образом. Хотя, если требуется построить, скажем, дерево поиска по каким-то другим данным, не вижу ничего плохого в использовании пар.
Ну да, пожалуй соглашусь с Вами и с автором. Пост очень хороший, по крайней мере для таких как я (начинающих разработчиков).
Вот самый, на мой взгляд, распространенный случай использования: если вам нужно вернуть из private метода пару значений. Класс создавать--излишне (и я, к сожалению, не помню, есть ли nested classes, как в C#, в С++), а out параметры--это, по-моему, хуже, чем pair, потому что больше похоже на грязный хак (как говорит дядюшка МакКоннел, программируй на языке, а не с использованием языка). Ах, да, и out параметры нарушают принцип единственной точки выхода из функции.
Ага, nested classes в С++ в наличии, но все равно для двух полей, возвращаемых из private метода, их создавать обычно излишне.
Макконнел, к стати, говорил в точности обратное — «Программируйте с использованием языка, а не на языке» :)
Возврат пары значений из private метода — это граничный вопрос. С одной стороны — метод внутренний и кому какое дело, что там происходит. С другой стороны — самого себя (через год) не жалко будет — разгребать десяток таких методов в одном классе в попытках понять, что вокруг происходит.
out параметры, это не грязный хак, стандартный подход, который используется в том числе и в libc.

Другой вопрос, что в STL не принято их использовать, и применяют в основном pair.
Все зависит от места применения. Например я использую кортеж для своего Rowset, для выборки из бд. А вот использовать в том варианте что описал автор никогда не буду и никогда не использовал.
Если нужно хранить список людей в контейнере std::map неизбежно придется создавать pair<...>(surname, name). Зачем тогда городить дополнительные классы?
Это будет хуже, так как код вида result.Surname нагляднее, чем result.second.
Получился отличный пример злоупотребления typedef'ом. Никогда не стоит так делать, потому что это лишь усложняет программу и не приносит вообще никакой пользы. Вам теперь придется знать не только что такое Person, но и что такое Name и Surname.
Заминусовавшие, пожалуйста, прокомментируйте.

Вот довольно неплохая аргументация Торвальдса на эту тему: lkml.indiana.edu/hypermail/linux/kernel/0206.1/0402.html, хотя немного превязанная именно к ядру Linux.

Что вы выиграете, написав следующий код?
typedef string Name;
typedef string Surname;


Вы сможете использовать Name вместо string, когда будете использовать строку для хранения имени. Что же это даст? Если вы вдруг захотите использовать другой класс для хранения имени, то нужно будет лишь поменять typedef. Только это будет работать лишь в том случае, если новый класс будет на 100% совместим со string. Зачем такое вообще может понадобиться? Если вдруг и понадобится, всегда можно заменить тип на другой вручную — это не такая уж и большая работа.

Какой же ценой дается такое сомнительное удовольствие?
— Вы усложнили программу, добавив в пространство имен еще два новых имени для типа string. Как известно, не стоит плодить сущности без необходимости.
— Другому человеку (или вам через пару месяцев), читающему ваш код, придется лезть и смотреть, что же такое Name и Surname. Если бы он сразу видел string, ему сразу стало бы все понятно.
— Вы все равно будете работать с этим типом как со string. Так или иначе, когда вы будете работать с переменной, объявленной как Name, вам придется держать в голове то, что Name это string и работать соответствующим образом.
— Предположим, нужно вывести имя большими буквами. Пусть есть функция string capitalize(string s), которая меняет все маленькие буквы на заглавные и есть Name name;. У вас два варианта:
Name capitalized_name = capitalize(name);
или
string capitalized_name = capitalize(name);
Какой же из них выбрать? Если первый, то получится, что string должен приводится к Name и обратно. Если второй, то зачем же мы тогда объявляли Name, если используем string?

Таким образом, используя такой typedef, вы должны будете работать с Name так же, как со string. Для этого вам придется держать в голове дополнительную информацию.

Если же вы будете просто использовать string, вы сделаете программу гораздо более простой для понимания. Если вы видите string, то вам сразу все ясно. Используйте имена переменных для описания их предназначения, а не имена их типов.
> Если первый, то получится, что string должен приводится к Name и обратно.

В этом-то всё и дело, в C++ typedef создаёт алиас для типа, и оба типа получаются взаимозаменяемы. Профита никакого, потому что можно написать:
typedef string Name;
typedef string Surname;
typedef pair<Name, Surname> Person;
Person GetInfo(int personId);

void foo()
{
  Person p = GetInfo(0);
  Name n = p.second; // а не .first
}


В Ada, например, можно создавать новый тип, который не будет синонимом для исходного.
> В этом-то всё и дело, в C++ typedef создаёт алиас для типа, и оба типа получаются взаимозаменяемы.

Да, само собой. Про «приводиться к Name и обратно» — это на случай, если typedef будет в будущем изменен на что-нибудь типа typedef MyString Name;.
Мне кажется, что этот вариант хуже, и чем вариант со структурой, и чем вариант с парой.
— Читабельность не улучшится: будут использоваться first/second, не давая информации о том что реально хранится в этом поле
— Вероятность совершить ошибку не изменится. Оба типа Name, String представляют из себя синонимы типа string. Можно легко присвоить имени фамилию, и наоборот
— Засоряется пространство имён типами, не несущими большой смысловой нагрузки
— Код не стал более коротким по сравнению с использованием структуры
Если следовать логике статьи, получится все равно плохо, так как во втором случае непонятно, чем является первая и вторая строка, и их легко перепутать.

Надо сказать, что именно для целей описать какой-нибудь объект, классы и существуют. Пары часто приходится использовать вместе с другими типами: map, hash table. Кортежи использовать не приходилось.
разве не очевидно, что GetNameAndSurname возвращает данные в порядке их объявления в имени?

GetTwoRandomNumbers — а что если потребуется ввести в игру третьего игрока? переписывать все места, где захардкожено, что игроков всего 2? функция должна быть GetNumbersForPlayers и возвращать массив значений.

Отвечу вместо автора его же цитатой:
«Наша игра только на двух игроков и никогда (by design) не будет возможна для большего количества — беспокоиться о расширяемости не нужно»
>разве не очевидно, что GetNameAndSurname возвращает данные в порядке их объявления в имени?
Вам очевидно, а мне вот нет. Отсюда вывод — в мире бывают люди, для которых это не очевидно. Ладно я — из вредности придираюсь, но вот есть люди, для которых родная письменность справа-налево — они вполне могут ошибиться.

На счёт игры — я исходил из того, что данная игра — по-определению на двоих (ну там морской бой или шахматы). Согласен, что пример, может быть, не идеален, но лучше что-то не придумалось.
для которых родная письменность справа-налево — они вполне могут ошибиться.
По-моему это было бы справедливо для функции getFullName, а вот getNameAndSurname вроде как определяет порядок.
В приниципе, Вы конечно правы, но если есть возможность сделать что-то более прозрачным, этим стоит воспользоваться. К тому же, как и написал автор, существует еще и проблема при добавлении данных.
Ваша проблема весьма надумана и успешно решается во многих функциональных языках программирования, откуда tuples и пошли. Так что не надо путать особенности языка с преимуществами и недостатками подхода.
Но здесь-то речь о С++, а не о функциональных языках. В плюсы tuples притянули «за уши», а плюшек функциональных языков при этом нет, вот и получается фигня.
Аналогично, притянули функциональный подход с функторами, std::for_each и т.п., а те же самые лямбды только в C++0x догадались добавить. Ну и как это использовать, dummy структуры на каждый чих создавать?
В некоторых случаях можно было обойтись bindами.
Ага, и устроить вывих мозга тем, кто будет потом читать такой код :)
Вопрос привычки и нормального форматирования кода. Конечно, если писать всё в одну строку, вывих обеспечен :)
Вопрос привычки — это если к этому привыкать ;) Я лично считаю, что bind1st и т.п. это извращение и не нужно это использовать вообще.
У меня мозг выворачивается такое читать:
cx = count_if (numbers, numbers+6, bind1st(equal_to<int>(),10) );
Уж лучше обычный цикл, честное слово. Либо лямбды, с ними читабельнее гораздо.
Это религиозный спор. Кому-то удобнее цикл, кому-то — вызов алгоритма. Если байндами никогда не пользоваться они будут ломать мозг. Если научиться ими пользоваться — они перестанут пугать и будут нормально читаться.
Вот только проблема в том, что даже если заставить себя разок сломать мозг и разобраться, начать пользоваться — то 8 из 10 коллег, увидев такой код будут говорить «фак мой мозг! Что ты тут нагородил — можно ж было цикл на 2 строки сделать и всё!»
А так видны ли плюшки?

val (FirstName, LastName) = getFirstAndLastNames()

На выходе у нас две переменные с однозначными именами. Кортеж существует только до pattern matching, программисту не надо с ним работать — всё сделает компилятор. В том числе и проверку типов.

На счёт C++ Вы, конечно, правы.
Как я понимаю, в этом случае pattern matching и есть плюшка, которая всё делает красиво.
В C++ есть эта плюшка, хоть это и не pattern matching:

boost::tie(FirstName, LastName) = getFirstAndLastNames();
Простите ради Бога — я и не думал критиковать подход! Статья размещена в блоге С++ и касается только этого языка.
Собственно вся поднимаемая автором проблема вытекает из непонимания того, что есть к ортеж, а следовательно, где семантически приемлемо его использовать. Кортеж условно можно считать безымянным функтором (логики), связывающим значения логически и во вполне определенном порядке. Поэтому для «имя-фамилия» использование кортежа неудачно, как сказал автор, порядок неоднозначен, а вот, например, для обозначения координат точки в заданном базисе вполне подходит, и куда больше структур и классов
Проблема вот в чём — встретив где-нибудь функцию вида pair<int, int> GetPointCoordinates(); нужно будет искать в ней (или где-нибудь еще) информацию о том, что всё-таки хранится в этой паре — координаты в декартовой или полярной системе координат, какая координата первая и т.д. Причём догадаться, где это искать, трудновато (в теле функции может быть написано просто return make_pair(0,0); — и думай тут что хочешь). А вот если функция вернёт структуру типа Point — то в ней уже однозначно будут либо Х и Y, либо угол и расстояние. Место получения информации одно и точно известно — структура точки. В случае использования пары таких мест может быть:
1) ни одного
2) одно чёрти-где
3) в каждой функции дублирующимся кодом или комментариями.
Все варианты плохие.
Эта проблема не решается и структурой Point, поскольку о том, полярные это координаты, декартовы, цилиндрические или какие еще, она не сообщает. Догадываться по полям структуры не слишком приятно. И здесь проблема вообще в том, что в хорошем коде не должно быть функций с именами вроде GetPointCoordinates(). Если для того, чтобы понять, что делает функция, нужно ее препарировать — это плохая функция. А вот если из контекста понятно, что функция, например, возвращает точку оптимума поверхности, заданной в евклидовом трехмерном пространстве, тогда структура Point не нужна — и так понятно, как трактовать ее результат.
Эта проблема решается структурой Point, так как она именами полей или комментариями сообщит эту информацию. Т.е. есть одно и логичное место, где автор может эту информацию написать, а пользователь прочитать.
И Вы верно подметили, что «если для того, чтобы понять, что делает функция, нужно ее препарировать — это плохая функция. ». Именно это приходится делать с кортежем. А со структурой — нет.

Что же касается контекста, из которого должно быть ясно, что «функция, например, возвращает точку оптимума поверхности, заданной в евклидовом трехмерном пространстве», то таким контекстом может быть:
1) Длиннющее имя функции — плохо
2) Документация, в которой это описано (а эту документацию еще нужно написать, обновлять, найти и прочитать) — плохо
3) Комментарий, который непонятно где искать (в месте создания кортежа, перед объявлением функции, при использовании кортежа) — плохо
Кстати, при использовании структур возникает еще такой неприятный момент: перед тем, как их использовать, нужно сначала посмотреть на их, простите, структуру. Сигнатура кортежа видна сразу же в месте его использования
Сигнатура то видна, но вот чтобы понять, что в нём лежит придётся читать код, который этот кортеж создаёт. А это:
1) долго
2) не всегда возможно
3) идеологически неверно
Именно об этом первые пару пунктов моих аргументов в статье.
UFO just landed and posted this here
В целом согласен, но для меня самое неудобство с использованием пар связано с .first и .second, т.к. не я всё время теряюсь что есть что. В частности, когда я делаю map<int, string> idToName; то потом при обходе итератором туплю, «it->first» — это что? Ах, это id. В этом случае нужно как минимум что-то вроде .key и .value. Ещё удобнее было бы вообще .id и .name, но тут уж точно придётся структурку городить.
Вся соль в том, что использовать нужно не _только_ кортежи, и не _только_ структуры, а комбинировать и использовать то, что в данный момент удобно. Так что по-моему тут сравнивать некорректно и все зависит от конкретного случая.
Мысли такого рода всегда верны. Мне просто наболело смотреть в коде на пары и кортежи, вложенные в вектора (с вложенностью иногда уровня в 3-4) — отсюда и статья. Случаев же когда в коде создан класс\структура там, где можно было обойтись парой\кортежем на порядок меньше.
Мне вот в последнее время кажется что ООП, и понятные структуры данных слишком сильно расслабляют мозг :)
Это я серьезно, люди которые привыкли к красивому коду, начинают тормозить когда видят нечто:
vector< pair< list, map< pair< int, int > > > HelloBrothers; со всеми приседаниями при прохождении этого в цикле…
А вот т.е. «красавцы» которые это пишут, в красивом коде отлично ориентируются. И для себя стараюсь держать медиану, таким образом получается код который не расслабляет и и не вызывает проблем в поддержке. Как кто-то сказал
«Настоящий ООП код, это когда смотришь на код, понимаешь что он делает, но не понимаешь как» :))
map< pair< int, int > >
красавцы говорят, что такое объявление в плюсах не скомпилируется
Угу, там ещё и list без параметра.
к сожалению посты нельзя править, я писал условно, естественно не комплируя, причем на понимание это не влияет, или вам кажется это важным?
вам виднее, но на мой взгляд ошибка достаточно наглядно показывает проблемы данной «стилистики»
ну так я и не спорю, самому не нравится
Мне вот «vector< pair< list, map< pair< int, int > > > HelloBrothers» — не нравится. Да, я на нём торможу. И еще половина моих коллег тоже тормозит. Зачем писать такой код, который без напряга смогут читать только профи? Где их набрать, этих профи, для работы над проектом?
Я понимаю — нужно поддерживать себя в форме, думать головой и т.д. Но, пожалуйста, не в рабочем проекте! Идите на topcoder и там изгаляйтесь.
код который не расслабляет

«Я комсомолец — я не могу без трудностей?»
Постоянный уход от сложного кода, реально снижает скилл :)). Это я серьезно, при этом понимаю проблему, что человек не настолько умен, чтобы еще что-то себе усложнять в коде. Хотя коллеги по работе, мне говорят(ли), что мой код очень понятный :)).
Обычно это касается локальных сложностей, т.е. мне нравится хитрый, но короткий код, и совершенно не нравится длинные копипасты и непонятные имена функций, отсутствие организации кода. Т.е. в двух словах это тяжело объяснить. Но наверное всем доводилось писать короткие и хитрые методы которые экономят силы, и приносят удовлетворение, вот такие мне вещи нравятся, иногда там могут присутсвовать такие же хитрые структуры данных, не вижу ничего плохого если они будут в рабочем проекте. Т.е. я не ратую за любого рода сложность в проекте, конечно же зло и снижает поддерживаемость. Надеюсь смог как-то передать, что я имею ввиду.
Хотел написать на хабр статью, касаемо обучения (може еще напишу). Так вот, обучение практически всегда (всегда?, ) сопровождается стрессом, которого люди старательно избегают.
Напишите статью, конечно.
Еще автор не сказал, что часто случается случай — имеем функцию:
pair< string, string > GetContact( int contactID ); // Возвращаем имя и фамилию


через 3 месяца указания, надо добавить отчество в список отображения. Ну и не долго думая кто-то решает сделать такое определение функции:
pair< string, pair< string, string > > GetContactID( int contactID ); // Имя, фамилия, отчество

потом телефон потом email,…
и реально функция ( не придумываю случались гораздо более «трудные» случаи ) может превратиться нечто такое
pair< pair< string, pair< string, string > >, vector< pair< int, string > > > GetContactID( int contactID ); // имя, фамилия, отчество, и счета (id, alias) контакта 

пока кто-то не отрефакторит и не сделает нормальную подходящую структуру.
UFO just landed and posted this here
Не понял, в чем смысл вашей фразы.
UFO just landed and posted this here
как вы добавите элемент в кортеж в данном случае, не создав вложенности?:
pair< string, string > GetContact( int contactID );
tr1::tuple<string, string, string> GetContact(int contactID);
?
Ну и дальше что? :) Вы спросили как сделать без вложенности пар, я показал. Про понятность такого кода в комментарии, на который я отвечал, речи не было.

По поводу использования pair/tuple в неподходящих местах могу только процитировать анегдот:
Пациент:
— Доктор, мне больно, когда я делаю так — и изгибается в бараний рог.
Доктор:
— Так не делайте так
вы показали, только на один шаг, это все равно что написать функцию
def fact(3) return 6
решение должно быть расширяемо, поэтому вы ничего не показали, а предложили отложить решение на один шаг.
В этом смысле это никак не отличается от структур: в структуре добавляется тип и имя в списке полей, в кортеже — тип в списке типов элементов.
Если нам внезапно нужны осмысленные имена полей, кортеж не виноват, что мы выбрали его вместо структуры.
Это отличается — если код уже вызывается в клиентском коде, везде придется править обращения к элементам.
В случае если сразу выбрать структуру в качестве возвращаемого значения, клиентский код переделывать не придется. Кортеж конечно не виноват, и как он вообще может быть виноват, виноват программист, который не посмотрел на пару шагов вперед, и это случается довольно часто. Да собственно и программиста обвинять не в чем, есть эволюция кода. Важно понимать где и что лучше использовать. В данном конкретном случае я бы даже не задумываясь определил структуру в качестве возвращаемого значения.
Это отличается — если код уже вызывается в клиентском коде, везде придется править обращения к элементам.

Если вставлять в конец кортежа то как был 0й элемент get<0>(foo) так и останется. Вот, если pair на tuple меняем, то будет занятно.

В данном конкретном случае я бы даже не задумываясь определил структуру в качестве возвращаемого значения.

Я тоже, если что.
Пример как раз не высосан, самый что ни на есть из жизни.
UFO just landed and posted this here
Что за тон?
Вы не понимаете или не хотите понять что я вам пишу.
UFO just landed and posted this here
Ну и заменили вы, теперь скажите всем прикладным программистам, что их программы перестанут работать и им нужно заменить pair на tuple. Хотя лучше сказать заменить на class или struct.
И как вы сказали закончить этот «цирк с конями». Не понятна ваша позиция, говорите что нужно завести структуру и при этом пытаетесь расширять данное ущербное решение.
Ну и если вы посмотрите комментарий к последнему примеру (чтобы было понятно что возвращает ), тоже интересно как это будет выглядеть без вложенности. Я не защищаю такой код, отнюдь, но в жизни случается как я сказал еще более тяжелые случаи. Если сомневаетесь, и считаете примеры надуманными, могу скопипаситить сюда здоровый кусок из реального продакшн кода, который надо переделывать и который пестрит подобными конструкциями.
UFO just landed and posted this here
В вашем примере структура не анонимная, это часть бизнес логики приложения, какого лешего представлять ее кортежем? В данном случае нужно завести класс Contact.

Вы живете в идеальном мире? Я сказал так случается и очень часто, не у меня, а у людей чей код мне приходилось читать ( править ). Естественно надо было завести структуру, или я где-то сказал обратное?

По поводу расширяемости — кортеж ничем не хуже структуры. Если измениться тип структуры, то вызывающий код должен быть соответствующим образом изменен, что-бы эти изменения имели смысл. Тоже самое и с кортежем, добавите вы один элемент, логика работы вызывающего кода тоже должна измениться, иначе это изменение не будет иметь смысла.

Кортеж хуже чем структура по расширяемости, тут вы не правы, т.е. если это касается с++ и
различных типов данных кортеж совершенно не подходит. Добавление поля( полей ), в структуре совершенно не обязывает менятся клиентскому коду, которому это поле не нужно. Повторюсь, я молчу о проблеме типов в кортежах (с++), поэтому не понимаю о чем спор.


UFO just landed and posted this here
OMG. Ну во первых, я уже 10 раз написал, что кортежи лучше использовать ad-hoc. В этом случае, у вас будет все в одном месте и не нужно будет менять код в 10 разных местах.

Думаю, и вы и я понимаем где лучше использовать структуры а где кортежи. Надоело спорить.
Но по поводу:

Если же вы используете кортеж вместо структуры, то скорее всего вам придется объявить соответствующий typedef и все будет точно так же как и в случае структуры.

Не хочу начинать новый спор. Но ведь и это не так, это может быть будет только похоже в случае с простыми структурами.
Ну, я писал о сложности расширения функционала. Мне просто приведённый Вами ужас даже в голову не пришел в качестве примера.
UFO just landed and posted this here
Спасибо, очень хороший комментарий.
На счёт шаблонной функции — действительно аргумент. Хотя как по мне, наглядность вывода кортежа универсальной функцией будет низкая (она ведь поля поименовать не сможет) — а в структуре всегда можно написать метод вывода в удобной форме.
Операторы присваивания и сравнения пишутся на автопилоте (даже не просыпаясь) любым программистом с годом опыта. А сейчас еще и некоторые средства рефакторинга и вставки сниппетов умеют это делать.
наглядность вывода кортежа универсальной функцией будет низкая (она ведь поля поименовать не сможет)

Кортеж из пар «имя»-«значение»? :)
Кортежы не должны требовать именования своих составляющих, ибо представляют собой только последовательности элементов, образуемых некоторым логическим правилом. Если именование требуется — это структура и кортеж лучше не использовать. Опять же, пример с координатами. Если я захочу в декартовых координатах сделать аффинные преобразования и перейти к другому базису, логично обозначить оси другими именами, нежели XYZ. При этом точка в этом пространстве все равно будет определяться тремя числами (и, кстати, не факт что одной природы), поэтому здесь использование кортежа лучше чем структуры struct Point { double x; double y, double z; }
Увы, все это из-за несовершенства синтаксиса C++. К примеру, нельзя (вроде бы) использовать структуры без определения:

void somFunction(struct options { int size = 2, string name = null, x = 0, y = 0 }) { ...};
struct { x = 0, y = 0 } getCoordinates() { ...; return { x: 12, y: 20 } };

и вызов:

someFunction({ size=2, x = 100, y = 150 });
int x = getCoordinates().x;

А ведь так удобно!!! Куда смотрели Керниган с Ричи. И ничего не мешает это сделать с учетом всех ограничений языка. Из-за таких ограничений и приходится бежать на языки типа PHP, где все можно.

И нет перебора по полям и методам (for i in ...) для классов и структур: из-за этого банальные вещи, типа сдемпить/сериализовать поля класса приходится делать либо руками, либо еще как-то. Почему бы рутинной работой не заняться компилятору??? А в PHP это делается одним оператором :(

А так, я бы еще задепрекейтил списки: в 99% случаев нужен поиск по номеру/индексу/удалениеи элемента — и там все это доступно только через полный перебор, так что получается заведомо неэффективный код.
Хорош уже сравнивать, php с с++. По крайней мере с++ создает порядок в голове, а php создает там хаос (если начинать с этих языков). Не говоря уже что php скриптовый язык. То, что вы перечисляете есть в boost и stl. Причем заведомо неэффективный код на с++ обычно на порядок быстрее php-шного. Ну а если разработчик с головой у него и производительность не хуже чем у любого скриптописателя. Для примера зайдите на CodeJam от Google и посмотрите решения уже прошедших конкурсов, посмотрите на чем пишут те кто в топ 100, когда гораздо важнее скорость решения а не скорость работы программы.
>>Куда смотрели Керниган с Ричи
По всей видимости вы путаете «си» и с++.
Или у вас бардак в голове.
Ну вы же сами написали, что PHP порождает хаос в голове :)
У Вас отсутствует понятие C++. Не пишите на нем
> А так, я бы еще задепрекейтил списки: в 99% случаев нужен поиск по номеру/индексу/удалениеи элемента
Как любитель функционального программирования, где списки являются основой основ, я бы сжег вас на костре инквизиции за эти слова
Тов. Lazin верно написал: кортежи отлично подходят для применения в шаблонах. По мне, это их основное применение.
Всё что имеет явную семантику, следует стараться объявлять вменяемыми названиями:
{int a1,a2,a3,a4;} -> {int centerX,centerY,widthX,widthY;}
Иначе уже через полгода все эти загадочные названия будут восприниматься как петроглифы.
Как по мне, описанная Вами проблема высосана из пальца. Если у программиста, который применяет пары и кортежи кривые руки — это проблемы программиста, а не языка.
Так и есть. Я ни в коей мере не наезжал на язык и не пытался его изменить. Я просто хотел показать программистам грабли. А уж наступать на них или обойти — каждый сам решит.
Никогда не видел указанных проблем в реальном коде. И да, для чистых данных, как правило, typedef'нутая пара/кортеж удобнее структуры ибо может унифицированно использоваться в шаблонах. Выделение же данных в структуру/объект предполагает, на мой взгляд, наличие какого-либо дополнительного поведения с ними инкапсулированного.
Какой-то надуманный пример. А вот это
string GetInfo( int personId );
что возвращает? Имя, фамилию или номер паспорта? Не в кортежах дело. В ФП их используют повсеместно и неудобств больших не имеют.
Странно, но если забивать гвозди микроскопом особенно в оконную раму, то это как минимум не удобно, но извернуться всеж наверно можно и расколотив ни одно стекло поиметь не плохую сноровку в забивании гвоздей микроскопом в оконную раму, только кому такой опыт потом продать-то… Однако, есть молоток, к тому же они бывают разных весов…
Язык C++ имеет очень много возможностей и существует множество подходов в программировании. Инструмент выбирается по задаче, а строгие суждения «хорошо» и «плохо» в таком гибком языке, как C++ они, на мой взгляд, не совсем уместны.
Так же положительный пример автора с парой случайных чисел в его же ключе можно и сделать отрицательным: значения хоть и случайны, но могут быть из разных диапазонов, например 1..10 и 50...250 и где-то в логике это мб учитывается, а потом вспоминай и лазь по коду смотри, что и как обрабатывается :) Так что смотря с какой колокольни смотреть на проблему.
Какие-то примеры в статье не выразительные. Вот если не будет кортежа в вашем первом примере. Ну т.е. вместо
pair<string, string> GetInfo( int personId );
написать
string GetInfo( int personId );
Неужели понятнее что возвращает функция? Имя? Фамилию? И то и другое через пробел? В таком печальном случае только комментарий к функции поможет.
Кортежи и пары — это примерно как анонимные определения структур. Поэтому их самое полезное применение — в реализациях классов, когда лениво писать отдельные структуры. Проще сделать pair и дописать рядом 2 строчки комментария о том, что там хранится и в каком порядке. Если потом нужно будет разобраться в коде, это будет несложно. Естественно, в public интерфейсе класса кортежи смотрятся очень плохо.
Sign up to leave a comment.

Articles

Change theme settings