Pull to refresh
231
0
Всеволод @torkve

Пользователь

Send message
>Вопрос к сообществу: какой вы считаете правильный перевод термина «future» в контексте статьи?

Имхо, не стоит его переводить. Википедия предлагает несколько переводов, которые где-то используются, но кажется, в реальной жизни они не используются. Терминал попался такой, который переводится плохо (переводы либо длинные, либо звучат тупо), поэтому везде устоялась калька «фьюче» и её более обрусевшие варианты типа «футуры», «фьючеры», «фьючерсы» (это ещё и с экономикой пересекается), можно выбирать любой.

P. S. В C++11 на стадии разработки предлагались типы std::shared_future и std::atomic_future. Вот не хотел бы я поледний переводить.
Есть ещё варианты в стиле «человек с опытом паскаля решил помочь в опенсорс-проекте или на работе перекинули за отсутствием других возможностей», вполне себе типичная ситуация, не раз видел. Синтаксис знакомый, общие принципы можно по-быстрому где-то прочитать, сел и пошёл писать. Или этот ваш «начинающий профессионал» профукал и забыл этот момент в учебнике, не все же отличники.
> Это уже не для новчика.
Ну, не знаю, мне кажется, что новичок вполне может сразу гуй писать. Это студенту такое не дадут, а заставят до посинения писать алгоритмы сортировки, но студент != новичок.

> Это, конечно, может быть проблемой
Главная на самом деле проблема — это то, что такое знание энфорсится в программиста. Т.е. это правило написано в каких-то книжках, на любом форуме вам авторитетно объяснят, что вы идиот и должны сначала были читать учебник, но в лучших традициях C++ требование не прописано в стандарте, программисту разрешается стрелять в ногу «и так тоже», и вся надежда на добрую волю IDE и компилятора с их подсказками (которые программист конечно же не факт, что прочитает или не проигнорирует).

> Ресурсов это не сожрет.
Как же не сожрёт, когда сожрёт? :) И размер объекта вырастет:
0 ➜ compileOnce 'class A { public: ~A(){}}; class B { public: virtual ~B(){}}; std::cout << sizeof(A) << std::endl << sizeof(B) << std::endl;'
1
8

И производительность ухудшится (накладные расходы на походы в vtable). Другое дело, что важным это замедление станет ещё хрен знает когда, но факт есть :)
А ещё я видел библиотеку, где объявление реализации интерфейсного метода с virtual приводило к сегфолту! (т.е. в интерфейсе он был не virtual). Но это уже совсем другая история.
> И в их числе должны быть не только «простота допущения ошибки», но и «простота её поиска\исправления», и «накладные расходы по её автоматическому выявлению»…

Это уже больше переход в обсуждение «чья реализация оказалась лучше», а не «почему так», я, пожалуй, в ней участвовать не буду :) Хорошо, что мы под конец друг друга поняли.
Ну тут как. Вы можете наложить на T ограничения (типа для всех T, которые реализуют какие-то определённые типажи), и если вы в дефолтной реализации хотите использоввать какие-то свойства T, то очевидно, что это придётся делать. Но в общем и целом получается, что да, всем, тут полиморфизм в стиле шаблонов C++, если бы для них наконец допилили концепты.
Я честно постарался и не смог придумать ситуацию, зачем мы можем не хотеть давать фичу всем классам, которые удовлетворяют требованиям этой фичи. По идее это плюс в любой непонятной ситуации, если мы не ограничиваем применение алгоритма конкретным типом.
Я тут мимо проходил, просто замечу, что реализация по умолчанию в расте делается один раз автором UsesFields как
impl<T> UsesFields for T {}

без дальнейших действий с точки зрения MyStruct. Частные специализации её просто перекроют, как шаблоны в C++.
> Мы не можем сказать, что было мотивом именно такой реализации системы наследования в расте, и не думаю, что, к примеру, Хор через пару дюжин дней или лет признается

Не, ну в голове-то он может думать что угодно, но процесс обсуждения и разработки в целом весь на гитхабе и на internals.rust-lang.org есть :) Оригинальное обсуждение дизайна я не нашёл, но вот, например, очень интересная старенькая дискуссия про добавление наследования структур. Оттуда ещё и можно ещё глубже по ссылкам сходить.

> Что до исходной аргументации, она так же не к месту. В Rust нет полноценного наследования. Есть реализация интерфейсов, то есть АОП, но не ООП. Сравнение плюсов с растам вообще не имеет смысла в этом контексте

Это, имхо, уже демагогия. Классическое наследование в ООП подразумевает наследование данных и поведения, в расте есть второе, но нет первого. Я слабо знаком с АОП и мне кажется, что оно вообще не про то, но готов поверить на слово. В любом случае, мы вынуждены сравнивать, просто потому что используем язык для решения задачи, а не потому что «там ООП». Даже исходный тред NeoCode начинается именно с жалобы, что ООП не такой.
Наложенные ограничения — это причина разницы, мы же работаем с последствиями.
Страуструп с вами, я не говорю, что виртуальный деструктор — это какая-то бага, что она меня бесит или что-то ещё в этом же духе.
Вся моя аргументация относится к исходному поинту дискуссии: в C++ есть потенциальные грабли с памятью при таком наследовании, неважно, неопытный/неумелый ты программист, или баг не отследили в процессе рефакторинг, важно, что этот проезд достаточно легко стриггерить. И система наследования в расте сделана так, чтобы этого проезда избежать, а не чтобы «не как в C++». Вот и всё.
Давайте попробуем сначала :) Попробуйте представить, что вы зелёный новичок, а я попробую рассказать, как он может воспринимать это место в C++.
Мы знаем, что есть класс, у него есть поля и методы. Мы можем создавать новые объекты оператором new и удалять — оператором delete. Можно создать поле-объект в конструкторе и удалить в деструкторе.
Ещё мы знаем, что классы можно наследовать. При этом если мы конструируем класс-наследник, у него вызовется и конструктор базового класса, и конструктор наследника. Аналогично с деструктором, мы просто унаследовались, и вот уже разрушается и наследник, и базовый класс.
Конструкторы и деструкторы для нас при этом отделены от обычных функций: мы знаем, что они вызываются каскадом, а обычная унаследованная функция не будет звать функцию базового класса, если только в ней явно этот вызов не написать. И вот в этом месте таится наш подвох: мы знаем, что есть виртуальные функции, которые нужны, чтобы из базового класса звать реализацию наследника, но при этом и в голову не придёт, что то же самое может быть нужно для деструктора (тем более, что виртуальных конструкторов вообще не бывает), потому что он вроде как и так зовёт обе реализации.
Более того, конкретно в этом месте можно напороться ещё и потому, что общепринятая инструкция про виртуальные деструкторы ничего не говорит про динамическое выделение памяти, а учит, что виртуальный деструктор вам нужен, если вы пишете хотя бы одну чисто виртуальную функцию. А у нас таких и нет.
> 1. Многие водители видят заранее, что человек будет перестраивать в их ряд
> 2. Многие водители заметив сзади лихача-шашечника переводит подсознательно фокус на него и считают траекторию. Т.е водитель уже готовится.

Кажется, ничего такого, чего не увидели бы камеры ИИ.
Деструктор есть. Но он внезапно должен быть виртуальным, чтобы такого не происходило.
Это ни капельки не логично, если задуматься. Более того, не знаю, что вы имеете в виду под «особой» инициализацией, но нам достаточно иметь в классе любое динамическое выделение памяти, чтобы получить отличный проезд:
class A {
    private:
        int *x;
    public:
        A() : x(new int(1)) {}
        ~A() { delete x; }
};

class B : public A {
    private:
        int *x;
    public:
        B() : x(new int(2)) {}
        ~B() { delete x; }
};

int main() {
    A *b = new B;
    delete b;
    return 0;
}
Вот в этом уточнении: «правильно написанный» — вся соль :)
Идея раста заключалась в том, чтобы не дать возможности случайно или по незнанию напороть ерунды в этом месте, неважно, понимаешь ты его полиморфизм или нет. Т.е. либо код не скомпилируется, либо какого-то проезда по памяти не будет.
Как ни странно, это сделано не просто так (в расте вообще нет вещей, которые делались с мыслью «давайте не как в C++»).
В расте нет наследования типов и есть наследование типажей, потому что первое в C++ приводит к различным проблемам с памятью (каждый программист на C++ в этом месте может и захочет сказать: «да надо просто знать несколько простых правил, а если ты их не знаешь, то и нечего программировать, это азы языка», но это плохая контраргументация против «нужно знать несколько простых правил обращения с типами в раст»).

В расте это место намеренно упростили, в результате чего поверх структуры можно реализовывать любые интерфейсы, наследовать функциональность, но при этом: практически* любой объект можно удалить фактически простым free(), а скопировать — простым memcpy(). И не иметь при этом проблемы с памятью и головную боль с виртуальным наследованием, виртуальными деструкторами и вот этим вот всем.

* конечно, для объектов типа fd/socket придётся реализовывать типаж Drop — аналог dispose в C# и close() в Java.

Фактически это и есть си с классами, только лучше (вспомните, например, как си живёт со структурами типа sockaddr_in и sockaddr_in6 в стандартной библиотеке: они вообще никак не наследуются, но имеют обязательный список одинаковых первых полей, а в памяти кастуются к «базовому» классу).
Не скажу за jabber.ru, я лично сталкивался с таким увлекательным рядом проблем:
* при отсутствии взаимной авторизации gmail может вообще творить что угодно (например, сообщение абоненту gmail будет возвращаться тебе в неизменном виде как бы от его имени, ему оно при этом не дойдёт. В обратную сторону сообщения при этом ходят)
* проблемы с видимостью пользователей в сети
* какие-то самопроизвольные действия с MUC (например, никнейм периодически принудительно меняется на имя-фамилия из гуглового профиля)
* ещё какая-то самодеятельность с контактами того же рода
* ещё видел смешную ситуацию, не уверен, может ли её сейчас стриггерить злоумышленик, вероятнее всего нет: если где-то в apps for domains в гугле включен жаббер-сервер для домена, то гугл плюёт на реальные записи DNS, отпинывает s2s коннекты от настоящего сервера этого домена и соответственно отказывается доставлять туда сообщения. Это конечно минорщина, но дебажить такое неприятно.
gmail.com имеет разнообразные увлекательные проблемы с s2s, да и вообще со стандартом.
Менее специфичный пример — это реализация итератора для своего контейнера, например.
Вы не то что неудачно выбрали оператор, а вы в каждом примере присваивающе-модифицирующего оператора вместо void или ссылки на результат присвоения возвращаете другой новый объект, что идёт вразрез со стандартной библиотекой и общепринятым подходом.

P.s. За сигнатуру
const bool operator ! (Vector3 &v1) {}
я бы повесил вас на сосне.
А что за ключевое слово «жареный»? Оно у вас уже встречается второй раз, но я ни разу не слышал про жареные функции в C++…
И где-то есть ещё и видео?!
Я давно это сделал чуть ниже.
Главная ошибка этой статьи заключается в том, что она подталкивает гипотетическую ЦА к имплементации операторов с ошибками и там, где это совершенно не нужно.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity