Pull to refresh
4

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

0,1
Rating
Send message

Я верно понимаю, что вы предлагаете пользователю иметь два телефона – один для соцсетей и лайканья, а второй, где озаботились безопасностью, под банковское приложение?

А ведь во времена MS-DOS машины как-то работали и без всего этого

Если мы вдруг откатим эти технологии вспять, вы осознаёте, какое раздолье появится для вирусов, развивавшихся годами? Вы готовы прямо сейчас опубликовать все ваши личные пароли в открытый доступ? Ибо такова цена

И большой вопрос в том, что разработчики просто не озаботились вкомпилить в бинарник только используемый код

А имеют ли разработки такое право? А готовы ли они принять последствия?

Совсем не новость. Например, была очень похожая статья за 2019 и у неё даже есть общий автор. Что уж говорить об истинных хранителях сего тайного знания – хозяевах котеек

Я это к тому, что если буфер не принимать по ссылке в DrawLibrary::DrawCircle, а вместо этого создать его в автоматической памяти внутри функции, он не будет константным и вы сможете его модифицировать, передав по ссылке в setDataInGivenPosition

Мне в целом непонятна идея иметь out-параметры в функциях и считаю, что в пользу такого дизайна интерфейса нужна очень убедительная аргументация

Но нарисовать туда не получится, поскольку функция setDataInGivenPosition хоть и constexpr, но не константная и меняет состояние объекта и вызывать её у самого constexpr объекта нельзя - но это и логично.

Разве эта проблема не решается возвратом буфера через возвращаемое значение, а не через входные параметры?

Изначально я подразумевал не столько конкретные прикладные задачи, сколько создание различных DSL и фреймворков для других программистов

Мне кажется, довольно широкие возможности плюсов в этом плане одновременно с возможностью писать относительно производительный код заметно повлияли на их популярность. И было бы хорошо, будь заниматься этим ещё и просто, но мне кажется, в дизайн языка придётся вложиться несоизмеримо больше в сравнении с существующей сложностью языка

Да, сейчас есть языки, которые сумели взять на себя часть задач плюсов, при этом имея лучший языковой дизайн, но мне кажется, что в этом направлении миру ещё очень долгий путь предстоит
ЕМНИП, это нужно было в шаблонах когда параметры шаблонного метода/функции/конструктора нужно было без потерь прокинуть куда-то дальше. Типа
Но ведь это и есть решаемая предложением проблема. При вызове метода на this, нам точно так же иногда требуется внутри прокинуть неявный параметр this куда-то. То же самое, как и для обычного параметра. Отдельно пример с and_then я уже привёл, комитет, видимо, посчитал, что таких примеров достаточно

Это как писатель, который решает, что его роман закончен. Тогда как издатель запросто может решить, что еще не пришло время для публикации этого романа
Но если верить сообщению выше, то «издатель» в лице комитета таки решил, что время «публикации» настало
Мне кажется, всё дело в том, что с концептами, Networking TS и Executors есть обсуждения, разногласия и понимание несовершенства тех или иных частей предложения именно внутри комитета — идёт активный диалог. И в таком случае, конечно же торопить не стоит

Но в случае с deducing this, кажется, все были согласны с предложением. Возможно, потому что именно люди из состава комитета чаще сталкиваются с проблемой и посчитали её, во-первых, достойной решения, во-вторых, достойной решения именно в таком виде, не найдя убедительной аргументации для отклонения предложения
В корне не согласен. Шаблоны нужны для написания алгоритмов, которые работают схожим образом для разных типов данных. Плюс возможность тонко настроить эти алгоритмы под конкретные типы.
Но разве метод, перегруженный по this не представляет из себя пример именно такого алгоритма?

Forwarding был предназначен для другого
А не могли бы вы сделать небольшой экскурс в их историю, я сходу не смог найти их исходное предназначение

Ага, но вы здесь путаете причину и следствие. Forwarding был предназначен для другого. Но, за неимением лучшего
Хорошо, я допускаю такую точку зрения, но это значит, что нам теперь нужно подходить к вопросу основательно и решать не только проблему перегрузки методов по this, но и любых функций по произвольному из аргументов. И решение должно быть консистентно, чтобы при этом не так сильно усложнить язык. Я пока не знаком с идеей, как это должно выглядеть

А теперь сделайте усилие и поймите, что предлагается не делать больше подобных экспериментов с C++
Но это только вы сочли предложение экспериментом. Автор же посчитал его вполне законченной идеей. Как скоро можно перейти от стадии эксперимента к законченной идее и принятию в стандарт?
И кто за это заплатит?
Значит, вы готовы решать сложные задачи за оплату и при этом желаете препятствовать развитию языка, которое упрощает эту работу для других?

И, естественно, ваша позиция выглядит именно как отказ упрощать, а не как попытка выбрать другой, более удачный способ решения, ведь вы не готовы предложить другой успешный вариант решения, всё, что вы можете — давать наброски, которые пока что не выдерживают критики на мой взгляд

возможно, со временем будет придумано что-то получше.
Как долго нужно выждать?
SFINAE было фичей? Мне всегда казалось, что это был костыль из имеющихся палок дабы не придумывать что-то нормальное.
Да, для решения проблемы, для которой использовали SFINAE, очень давно задумывались концепты. Так уж получилось, что их очень сильно упростили и они попали только в C++20, хотя и были на пороге стандартизации ещё в 2009 году
Если честно, я не увидел, что такое ActualType и чем принципиально отличается от MyType

Соответственно, что произойдет с this после вызова g зависит полностью от сигнатуры и содержимого g.
Но g может иметь реализацию одновременно и для lvalue и более эффективную для rvalue. В идеале хотелось бы выбрать наиболее оптимальную из допустимых

Я это к тому, что предложение позволяет писать такой код:
template <typename T>
class optional {
  // ...
  template <typename Self, typename F>
  constexpr auto and_then(this Self&& self, F&& f) {
    using val = decltype((
        forward<Self>(self).m_value));
    using result = invoke_result_t<F, val>;

    static_assert(
      is_optional<result>::value,
      "F must return an optional");

    return this->has_value()
        ? invoke(forward<F>(f),
                 forward<Self>(self).m_value)
        : nullopt;
  }
  // ...
};

где нам совершенно точно нужно, чтобы self умел быть в том числе и MyType&&, но разыменовать *this в rvalue для этого нельзя в текущих правилах языка
Давайте добавлять новое осторожно и желательно не так, чтобы новая фича хитрым образом решала кучу разных проблем. Т.к. последствия сложно предсказать. Примеры в прошлом уже были: открытие метапрограммирования на шаблонах и SFINAE
Но SFINAE-то было фичей, решающей именно что одну задачу. И именно сейчас авторы предложения рассмотрели большее число возможных последствий, которые приходят с новой фичей и постарались их предсказать. Например, её сочетание с виртуальными методами посчитали слишком непредсказуемым на текущий момент и отложили в сторону, чтобы случайно не усугубить ситуацию. Часть же из последствий посчитали скорее полезными, чем вредными

принцип наименьшего удивления
Но именно старое поведение нарушает его в современных реалиях, где весь код обмазан шаблонами

«Работает? Не трогай»
Считаю это наименее удачным аргументом где бы то ни было

Более того, волею судьбы я вхожу в их число. Но думаю, что мнение 10% можно пожертвовать, чтобы сделать жизнь оставшихся 90% проще. Лично я готов выписывать руками перегрузки для &, &&, const & и const &&, но не заучивать наличие еще и новых шаблонных методов.
Не могли бы вы дописать их для std::filesystem::path::operator string_type()? Так уж оказалось, что авторы стандартной библиотеки, в отличие от вас, не могут, не хотят или просто утомились и забыли

Но при этом для вас for(const &, const &&) неприемлем, хотя здесь все гораздо проще, чем со spaceship operator
Проще как абстрактная идея в контексте C++20, но сложнее в сравнении с обсуждаемым предложением в стандарт

это неправомочное применение уже существующей конструкции
Нет, это не так и точнее я аргументацию постарался выразить в выше

нет моего варианта, это лишь наброски фантазий, которые призывают задуматься о том, что можно по другому
Да, можно и я указал, что часть ваших аргументов актуальна. Но я не нахожу способа сделать по другому, который был бы проще предложения, даже актуальное поведение языка, выражающее то же самое, я нахожу более сложным. И если я не могу найти, то единственное, что можете вы — помочь его поискать, но ваши наброски имеют в своей сути недостатки, на которые я и стараюсь указать в ходе диалога
В большинстве сценариев использования языка пользователю не нужны ни forwarding references, ни perfect forwarding. Для многих сами эти понятия — это уже матан какой-то.

Я бы предпочел язык, который действительно делает простые вещи простыми, а сложные возможными.

Но язык и так решает сложные вещи возможными путями. Так уж случилось, что C++ оказался удачен для решения преимущественно сложных задач. А если вы хотите ещё и решать сложные проблемы просто — это очень и очень сложно. Как минимум, нужно чтобы кто-то придумал такие решение, но почему-то в них часто находятся существенные изъяны и они отклоняются

Например, как определить, если ли у типа метод с определенной сигнатурой? Есть ли у нас в type_traits простой инструмент типа has_method?
В C++20 это выглядит вот так:
requires (A a) { a.foo(); }


И нет, я не вижу здесь никакой связи с каким-либо forwarding-ом.
Вероятно, именно в этом наш взгляд на проблему принципиально отличается. Шаблоны уже созданы, чтобы решать проблему ручной копипасты, т.е. того же, для чего вы предложили for после списка аргументов. А forwarding references уже используются для уменьшения копипасты конкретно в случаях перегрузки по константности и value category. Я думаю, что программист столкнётся раньше с проблемой написания правильного конструктора, поддерживающего копирование и перемещение, чем метода. А там он уже познакомится с forwarding references. Но по странной воле случая, для произвольного аргумента у нас есть forwarding references, а для this не было. Теперь будет

Я бы сказал, это не новый синтаксис из предложения какой-то особенный и привносит что-то новое, наоборот, в нём ничего нового нет, это старый дизайн языка имел особенный магический и при том ограниченный синтаксис, по умолчанию позволяющий вызвать lvalue метод на rvalue объекте, нарушая семантику операции

Просто к старой форме записи добавили более логичную, консистентную и мощную на базе уже существующего и знакомого синтаксиса
И попытка показать, что если не стремиться одним решением закрыть кучу проблем (еще раз ткну вас в SFINAE как в яркий пример), то можно попробовать найти более понятное для обычных программистов решение.
Но вы не показали, как можно попробовать найти более понятое решение, ведь до этого вы не предложили ничего:
А как ваше предложение работает в теле метода?
Нет моего предложения


Кроме того, вы опять обращаетесь к подмене тезиса и апеллируете к SFINAE — инструменту изначально не задуманному, для решений тех задач, для которых его применили. Да, это неудачный дизайн, но это плохая аналогия для deducing this, который целенаправленно намерен решить ряд проблем, а не делает это случайно

объяснить рядовому программисту будет проще, чем вот это:
Опять же, вы пытаетесь объяснять проблему, возникающую преимущественно на стыке с perfect forwarding программисту, не знакомому с perfect forwarding и гордо объявляете, что ваша конструкция понятна программисту, который не будет её писать. Нет, конструкция должна быть понятна в контексте, проблема которого решается. И программисту, понимающему проблему perfect forwarding, эта конструкция будет понятна и знакома

Кроме того, вы допустили ошибку в коде:
template<typename T> void f(this const & self) {...}
Он будет выглядеть на самом деле вот так:
template<typename T> void f(this MySpecificType const & self) {...}

Это больше похоже на обычный метод с обычным параметром, только помеченным словом this и содержит меньше непонятного

В языке уже есть decltype(auto)
И это пример ужасного дизайна, непохожего ни на что

Точно так же decltype(*this) на этом же месте будет иметь собственный смысл.

Если вас так напрягает decltype(*this), то можно сделать новое ключевое слово co_variant:
co_variant(*this) f() { return *this; }

Допустим, я переписал код вот так:
co_variant(*this) MyType::f() { 
  g(*this);
  return ???;
}

Во-первых, мы вообще-то, не обязательно и хотим делать возврат, значит, вариант с возвращаемым типом нам не подойдёт.
Во-вторых, для MyType&&, будет объект, на котором вызван метод, скопирован в g или перемещён?

Если будет перемещён, то это поменяло смысл кода, позволив указателю разыменоваться в rvalue, на что я и указал ранее как пример неконсистентности

Если не будет, то этот пример не решает ту же проблему, что стояла перед авторами предложения
Не уверен, что понял вопрос. Объяснять ref-qualifiers не просто. Объяснять людям шаблоны не просто.
Верно, потому что они не только сами по себе сложны, но ещё и решают сложную задачу — perfect forwarding

Поэтому, когда вы пробуете заявить, что новая конструкция сложна — её сложность не стоит оценивать в отрыве от решаемой задачи. Объяснить вышеуказанную конструкцию совсем не сложно в контексте forwarding references, которые пользователю ещё раньше встретятся в контексте perfect forwarding

В то же время, ваше предложение потребует весь тот же набор знаний на входе, но не сможет их использовать, как основу для формирования новых знаний ввиду того, что создаёт новые специфичные конструкции

В том, что эта разница внезапно появилась. Ее не было, а тут хоба! И принципиально новая фича, которая нужна, образно, в 10% случаев
А теперь сравните это с ref-qualifiers или своим примером, где при рассмотрении разница точно так же появляется, но в значительно больших объемах и/или с совершенно отличными синтаксическими конструкциями без обоснования, зачем они нужны

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

Наличие неявного this — это фундаментальная фича языка
Нет, это просто особенность дизайна, которую поставили под сомнение в данном предложении. А теперь и в стандарте

С нее начинается изучение ООП в C++
Вместе с предыдущим предложением и далее — argumentum ad antiquitatem, вы не можете взять произвольную логическую уловку и использовать её как аргумент

которые были актуальны для 10% пользователей языка.
Вам не кажется, что 10% программистов на C++ — это крайне внушительное число? А с учётом того, что код именно этих программистов в конечном итоге обычно будет вызываться из программ рядовых программистов — это куда более значимая часть сообщества и проблемы

Ok, ВНПЭД.
Я попрошу аргументировать по существу, поскольку я постарался дать максимально убедительную аргументацию, почему этот случай мной интерпретируется иначе. Если вы не согласны с аргументацией — укажите на ошибки в ней

Обратите внимание, что вы согласны принимать новые элементы, когда это нравится вам.
Они мне нравятся, когда используют понятные и уже знакомые программистам механизмы из этого или других языков, или даже расширяют их, снижая тем самым порог входа. Так же, я готов принять новые конструкции, если они фундаментально меняют взаимодействие с языком (лямбды, концепты, модули, spaceship operator) или имеют совершенно новую семантику (атрибуты)

Похожесть на другие языки — так себе аргумент, т.к. мы говорим во что превращается C++, а не другие языки
Дизайн языка — сложная задача, и бежать, не оглядываясь на существующие практики и на реальность (популярность решений в других языках) глупо. Если вы хотите создать сложный язык, затрудняющий переход на него ещё и опытным программистам других языков — ваше право, но не удивляйтесь, если на него откажутся переходить новые программисты из соседних сфер и он превратится постепенно в COBOL из-за отсутствия развития

добавление подобных неоднозначных фич в язык неизбежно будет вести к увеличению количества недовольных текущим путем развития языка
Насколько я могу судить, у нас всего три возможных варианта, каждый со своими преимуществами и недостатками:
  • не добавлять фичи — можно, но вы постепенно растеряете тех участников языка, которые создают значимую часть библиотек для остальной части пользователей, я не рассматриваю это как вариант, который можно выбрать, поэтому не вижу смысла в вашей апелляции к этому невозможному варианту;
  • добавлять более простые фичи — на вашем примере это оказалось неудачным подходом, имеющим значительные недостатки, что я и пытался донести выше, я открыт к обсуждению этого варианта, но пока что не увидел убедительных аргументов в его пользу от вас;
  • добавлять такие фичи — вы верно указали на некоторые недостатки подхода, но предыдущие два пункта не являются адекватной его альтернативой


А посему неплохо было бы новые фичи, которые хотят добавить в язык, оценивать гораздо более жестко, чем это происходит сейчас
Стоит заметить, что такие фичи и рассматривают довольно жёстко, некоторые из них не принимаются десятилетиями, многократно упрощаясь перед попаданием в стандарт языка. Просто, вероятно, не является эта фича на взгляд комитета неудачной, как считаете вы
Там decltype(*this) указывается в качестве типа возвращаемого значения.
А как ваше предложение работает в теле метода? Что если мы не возвращаем значение, а хотим определить переменную того же типа внутри метода? А чем будет делать просто *this внутри метода? То же, что и раньше? тогда *this не будет иметь ничего общего с decltype(*this)? Нам только запомнить такую особенность?
Ничуть не хуже, чем объяснять человеку, в чем разница между двумя этими шаблонными методами
Считаете ли вы, что объяснить function ref-qualifiers или шаблоны просто (здесь я в том числе подразумеваю forwarding references)? А если программист знаком с последними, то в чём конкретно состоит сложность объяснения разницы между этими двумя шаблонами? Я вижу относительную сложность только в том, что в T может оказаться тип-наследник. Но то, что в шаблоне может оказаться совершенно неожиданный тип не должно удивлять того, кто знаком с шаблонами — всё как с обычными параметрами функции

а почему только первым?
Потому что это привычная многим программистам концепция. Возьмите для примера Python, где self тоже первый

И что у нас для g три формальных параметра, а по факту внутри их всего два
Я вижу три, не подскажите, в чём я заблуждаюсь?

И даже наоборот, это сейчас нужно вдаваться в подробности и объяснять, что this у нас является неявным параметром, который не указан, но он есть — просто запомните, что здесь не как у обычных функций

может быть уже и указателем на копию объекта
Не может. Условный self может быть копией объекта. Да, как и любой обычный параметр функции

И что в итоге у нас f и g — это вроде как методы demo, но указатели на них разные
Да, методы, но у f какой-то очень странный и сложно устроенный указатель, а у g — самая обычная функция. Последнее привычнее и удобнее в использовании, не правда ли?

Вас, кстати, не смутило, что теперь в C++ можно [[nodiscard]]
Нет, не смутило. Во-первых, это совершенно новая концепция в языке, существующая до этого только в implementation-defined виде. Не удивительно что её синтаксис особенный. А ещё не смутило ввиду убедительной мотивации от авторов предложения. И заметьте, в сущности, это одна фича, решающая ряд проблем, чем не может похвастаться ваш пример

На мой взгляд, нужно искать компромисс
И, кажется, эта бумага и является компромиссом. Мы получили более простой вид методов, ведущих себя как обычные функции, но и при том лишённые возможности быть объявленными как виртуальные

что-то должно решаться введением новых элементов языка (типа атрибутов, fold expression или range-based for)
Вы апеллируете к тому, что fold expression и range-based for были новыми элементами, но обратите внимание, что fold expression похож по своей семантике на pack expansion, а значит привычен пользователю, range-based for похож не только на for, но и на for in из других языков, что тоже упрощает вход. Deducing this тоже похож — во первых, на другие языки, во-вторых, на собственные же свободные функции. А на что похож ваш пример? Какой опыт программисту упростит понимание вашего примера?

Вот да, упоминание SFINAE как раз к месту. Это же яркий пример того, как одним механизмом пытались решить сразу несколько проблем
Вот только дело в том, что… нет, не пытались, это получилось случайно и случайно открытые мощные возможности SFINAE оказались востребованными у программистов. Но исходная мотивация такого поведения была несколько проще и решалась иная проблема, а потому я считаю это сравнение неудачным и воздержусь от его комментирования

Судя по дальнейшему обсуждению этой темы ниже в комментариях, у вас самого с пониманием сложности.
В таком случае, прошу указать мне на конкретные проблемы с пониманием, чтобы я мог счесть этот аргументом убедительным, а не argumentum ad hominem

Образно говоря, ситуация такая, что для входа на кухню нужно сделать сальто вперед с разбегом в два шага
Я предпочту отказаться от комментирования аналогий. Они красивы, ярки, но мой опыт дискуссий показывает, что такой спор очень быстро перестаёт быть конструктивным и перетекает в область бесконечного спора о корректности самих аналогий и терминов, а не по существу

И я бы хотел иметь просто forward и move, без необходимости разбираться с (A&& -> A&) и прочей лабудой.
Не пробовали оставить идею на stdcpp.ru и получить фидбек? Быть может, мы получим желаемое вами поведение уже в следующем стандарте?
Нужно — не забывайте, что менять контейнер во время итерации всё ещё нельзя
Итераторы всё же являются довольно глубокой концепцией, и на первых шагах знакомства с C++ проще запомнить магическую конструкцию for(auto&& v: vec) с запретом на модификацию контейнера, чем постигнуть всю философию итераторов в теории и на практике. Так что я всё ещё считаю, что эта конструкция снижает порог входа

и чем они отличаются от обычных переменных (нельзя захватывать в лямбды, например)
К счастью, это недоразумение починили в C++20

Я бы сказал, что средний C++-программист не обмазывается if constexpr и концептами.
А вот читать их ему в какой-то момент после ошибки компиляции придётся и концепты здесь немного, но будут легче в понимании в сравнении с экзотическими конструкциями на SFINAE
Нет, я не хотел написать std::forward, выражение a принципиально отличается от static_cast<decltype(a)>(a) с точки зрения языка, вот пример того, как эта конструкция меняет поведение программы

Information

Rating
5,225-th
Registered
Activity