Теперь получается, что, "написана ли программа на C++ или нет", зависит не от её кода, а от набора внешних по отношению к ней входных данных
Всё верно, гарантии можно обеспечить и неязыковыми инструментами. В том числе посредством приставления охранника с дубинкой к каждому пользователю, чтобы валидировать ввод. Однако, это кажется странным только при доведении до абсурда
А если вам, например, нужен эффективный инкремент знакового счётчика, в этом случае довольно осмысленно завязаться на тот факт, что на современном оборудовании его переполнение займёт десятилетия. Но если вы выждали десятилетия, или запустили программу на инопланетном оборудовании и таки переполнили счётчик, внезапно сделав программу не программой на C++, то... ну, вас в меньшей степени будет волновать вопрос того, написана ли программа на C++
И дополнение. Ваш вывод не совсем верен. Согласно стандарту, написана ли программа на C++, всё же, зависит не совсем от внешних факторов. Как только UB стало неизбежно, программа ретроспективно перестаёт быть программой на C++. Т.е. никогда ею и не была, независимо от внешних условий
Здесь, конечно, можно было бы порассуждать, что конкретно перестало быть программой на C++ — программа в рамках этого запуска, каждая копия этого исполняемого файла или вообще каждая функционально эквивалентная программа в мире, но вряд ли стандарт даст вам ответ на этот вопрос. Программа-то больше и не программа, и стандарт на неё не распространяется
Картинка в 200 фпс в играх не являются достаточными условием для плавного геймплея, поскольку для него нужна ещё и обработка физики с высокой частотой
И под "чисто маркетинговый показатель" подразумевалось именно несоответствие одного другому, поскольку этот показатель не может отобразить плавность/комфорт игры в отрыве от частоты обработки физики, который не указан. И такие показатели как раз и принято называть "чисто маркетинговыми"
Вы же, прочитав текст, не вникая в его смысл, сформировали у себя в голове почти противоположный тезис "картинка в 200 фпс в играх не являются необходимым условием..." и оспариваете его, хотя он заявлен не был автором
Я просто пытался сказать, что содержимое заголовка и текста абзаца не вполне соответствует бенчмарку (ибо дело не в согласовании signed/unsigned, а конкретно в unsigned, код с которым, предположительно, окажется медленнее, даже если везде будет unsigned)
и использование таких операндов приводило к тяжеловесным ассемблерным инструкциям приведения типов от одного к другому
Только вот в современном мире почти везде используются two's-complement-числа, для которых это приведение типов — no-op. И, вероятно, бенчмарки показывают такую разницу не из-за разности типов, а из-за, собственно, unsigned, код вокруг которого, предположительно, хуже поддаётся оптимизации из-за допустимости его переполнения с точки зрения стандарта (в отличие от signed, переполнение которого не определено стандартом)
Тот же докер, чтобы поставить на убунту, нужно пройти небольшой квест
Вы же в курсе, что докер есть в официальном репозитории вашего дистрибутива и ставится единственной командой, не требуя ничего из перечисленного по вашей ссылке?
apt install docker.io
Просто завязывайте с виндовыми привычками читать сайты разработчиков, которые вам подсовывают левые установщики и неофициальные репозитории
Я тоже люблю винду похейтить, однако... У меня винда ставится за несколько минут. Да, это не минута, и nvme у меня в ноуте по-быстрее будет, но, простите... А средний юзер действительно заметит оптимизацию скорости установки винды с пяти минут до одной, пока заваривает чай? Я уж молчу, что большинство юзеров будут ставить себе винду примерно ноль раз за всю жизнь
И действительно ли этот результат стоит потенциальных неприятных последствий переписывания логики установщика?
Ну да, ну да, кто-то пишет if(!result) вместо if (result != http::ok), чтоб потом узнать, что http::ok внезапно 200, а не 0. Не надо так
А вы http-коды храните в интах? Тогда у вас более серьёзные проблемы, чем обсуждаемая выше, до которой ещё дорасти нужно
Но вот чем принципиально для вас отличается if (opt.has_value()) от if (vec.isEmpty())?
Тем, что одно из условий положительно, а другое негативно. Главное, что оба выражены прямо так, как подразумевал программист. А вот стоит только взять if (!vec.isEmpty()) — здесь уже проблемы, описанные мной выше. Имеем ввиду наличие элементов, спрашиваем про отсутствие
Любой из двух следующих вариантов я считаю предпочтительнее:
auto has_element = !vec.empty();
if (has_element) /* ... */;
if (vec.count()) /* ... */;
Ещё лучше было бы, предоставляй стандартные контейнеры сразу оба — и empty, и has_value
Можно ещё сравнить std::optional и std::any
На самом деле нельзя. У них семантика разная, std::optional семантически предполагает отсутствие значения, это фича для конечных пользователей. А для std::any (как и std::variant) это вынужденная фича, необходимая для поддержания мув-семантики, а не для пользователей. Т.е. не предполагается, что пользователь будет пользоваться has_value/valueless_by_exception как нормальным интерфейсом, ему это в норме и не нужно. А соответственно и неявный каст не нужен — это не часть нормального повседневного интерфейса, так что ответ на вопрос...
Вас не смущает использовать разные способы проверки одного и того же?
Однозначно нет. Пользователь для std::any в норме не пользуется этим интерфейсом. Более того, в архитектуре даже худших среди кодовых баз, с которыми я работал, ещё не было столь серьёзных проблем, чтобы мне нужно было использовать "такой" std::any
А там, где он использовался не как архитектурный костыль — там у меня была гарантия не только на конкретный тип в нём, и на то, что он точно всегда имеет значение
if (страна.город.мэрия.зачем.так.много.вложенности.удалена == false)
и это:
if (not страна.город.мэрия.зачем.так.много.вложенности.удалена)
Если в первом случае отрицание далеко от непосредственного контекста, то во втором оно не на том месте и отделено от него синтаксическим шумом. А теперь просто сравните с
if (страна.город.мэрия.зачем.так.много.вложенности.неУдалена())
Может, настало время добавить немного выразительности в интерфейсы своих типов?
Как альтернатива, когда типы трогать не хочется, воспользуйтесь древней технологией — переменной:
auto&& foo = страна.город.мэрия.зачем.так.много.вложенности;
if (not foo.удалена())
Не только в программировании, но и при изучении, обдумывании и чтении, мы обычно строим не просто некоторую ментальную модель, а часто стараемся приводить её к иерархическому виду. Например, можно вспомнить, что структурированные правила из учебников учатся и воспринимаются сильно легче, чем разрозненные факты или тем более исключения из них (вспомните, легко ли учились спряжения)
Казалось бы, к чему я вообще о какой-то иерархии в этом вопросе и где здесь иерархия? Всё просто — рано или поздно мозг программиста начнёт любой if (something) обобщает до до некоторой общей абстракции. Не думаю, что у неё есть название, но я бы её выразил как if ok/if not ok. Когда мозг их обобщил, становится очень просто читать такой код. И дело не в его длине, а в том, что все подобные конструкции мозг начинает маппить на то самое внутреннее обобщение
Но при этом, когда вы местами используете if (opt.has_value()), местами if (ptr != nullptr), где-то if (result != http::ok), где-то ещё что-то... Мозг просто хуже это делает — вместо одного обобщения он работает лишь с частными случаями всюду. А исключения и частные случаи мозг не очень любит
Но почему мозг точно так же не может обобщить разные примеры выше?
Потому что ещё был упомянут альтернативный вариант проверки, if (opt != std::nullopt). И это просто ужасный, кошмарнейший пример, связанный уже не с длиной кода. Эта строчка подразумевает, что внутри ветки мы работаем с happy path. И автор имеет ввиду положительный путь исполнения, и код пишет под положительную ветку исполнения. Но в условии при этом он спрашивает буквально "не является ли путь негативным". Изначальное семантически чисто положительное условие превратилось аж в две инверсии. Имеем ввиду положительное — спрашиваем дважды негативное. И, хотя формально две инверсии не меняют семантику на негативную, мозг, видя инверсии и негативизацию в условии... считает их чётность... и считает крайне плохо, и сбоит. Видит отрицания — думает про unhappy path. В первую очередь, конечно. Во вторую таки считает чётность, откатывает в своей ментальной модели то, что навыдумывать успел... Много избыточной ментальной работы, много кеш-миссов в голове
И это ещё не всё. Теперь комбинируем все перечисленные проблемы вместе и получаем ещё одну: if (opt != std::nullopt) и if (opt.has_value()) выражают одну мысль, но совершенно разным языком, который не обобщить до одной понятной концепции в своей ментальной модели. И вместо того, чтобы читать произвольную ветку как if (ok), ваш мозг будет вынужден каждый раз дополнительно разбираться, что значит второй аргумент в сравнении, к какому "виду" сравнений оно относится, считать чётность инверсий и перепроверять себя, забивая ментальный контекст этой мусорной работой и утомляя вас
Чем больше вы утомляетесь, тем больше ошибок совершите, разных и в разных местах
Какой вывод? Выражайте мысли прямо, чтобы ошибаться меньше. И if (something) — самый простой способ выразить мысль прямо. Автор будет меньше раздумывать, каким именно правильным способом это написать (правильный останется единственным, если вписать в стайлгайд и распространить на всю кодовую базу), а значит и с меньшей вероятностью совершит ошибку из-за семантического насыщения. А пользователь тоже будет читать привычный паттерн на автомате, снижая риски запутаться при виде всех этих таких похожих визуально, но таких семантически разных != nullpt, != http::ok, != nullopt. Ой, а в средний закралась ошибка. А вы её заметили?
В таком варианте при прочтении кода лучше видны не стыковки.
Это иллюзия, проявившаяся из-за того, что вы прямо сейчас погружены в контекст проблемы, понимаете проблему и думаете, что вне этого контекста так же легко заметите проблему. Стоит вам только забыть контекст и спустя время вернуться к коду, увидев такие строчки...
if (SomeGlobalPointer == nullptr)
{
delete[] SomeGlobalPointer;
}
...никаких несостыковок вы тут не заметите. И явность вам никак вообще не поможет. Потому что проблема не в отсутствии явности, а в том, что мозг крайне плохо обрабатывает в условиях инварианты, завязанные на инверсию
Лично мне же такой код ещё и усложняет чтение. Ведь для кода в голове обычно строится некоторая ментальная модель. И она строится не на конструкциях языка. В моём, примере, мозг поступает иначе — определяет зависимости между телом ветки и содержимым условия. Это необходимая для понимания сути кода низкоуровневая часть ментального процесса. И независимо от того, способны ли вы её пронаблюдать в своём мозгу сознательно, она на некотором уровне работы мозга отрабатывает
И если в исходном условии зависимость лишь одна — SomeGlobalPointer, возможных исходов в ментальной модели тоже два — либо у нас SomeGlobalPointer, либо у нас не SomeGlobalPointer. Добавив же == nullptr, мы привнесли в ментальную модель ещё как минимум одну сущность. И не важно, что это неизменная часть условия
В нашей ментальной модели теперь сущностей, от которых зависит исполнение тела, стало в два раза больше — две. Также и возможное число исходов (где-то в фоне считаемое мозгом) увеличилось до четырёх — SomeGlobalPointer, не SomeGlobalPointer, nullptr, не nullptr
Это лишь увеличит когнитивную нагрузку при том же семантическом наполнении кода. Вы будете уставать чуть быстрее. Уставая же, будете совершать новые ошибки. Решали одну конкретную частную проблему — добавили новых неочевидным для себя образом
Несколько ноутбуков, среди которых я выбирал несколько лет назад, были дороже в комплектации "без ОС", чем с Windows. Так что чисто технически, Windows зачастую имеет отрицательную стоимость для конечного потребителя
TL;DR: пайп позволяет визуально отделить вход от выхода и совершать меньше действий в процессе правки команды
В своём рассуждении, вы ловко выкинули из примеров команд pattern:
grep pattern file
cat file | grep pattern
А ведь чаще всего вы будете нажимать стрелку вверх и менять именно паттерн (или даже весь grep) при том, что имя файла будет оставаться фиксированным. Но каждый раз, нажимая стрелку вверх, ваш курсор будет оказываться в конце строки, т.е. рядом с file, и вам каждый раз нужно будет перепрыгивать его, чтобы добраться до паттерна, который будет за сессию работы меняться куда чаще. Лично мне лишние прыжки через имя файла удовольствия не доставляют, поэтому я сознательно использую cat. Вы, конечно, можете опротестовать и предложить мне более эффективную (с точки зрения машины) альтернативу:
< file grep pattern
Но. Стрелку легко перепутать, уничтожив файл. Во-первых, стрелки рядом на клавиатуре; во-вторых, даже будь они далеко — перепутать их всё ещё можно. Ещё здесь нет явного и заметного разделителя, который было бы удобно искать глазами — имя файла миксуется с командой; в случае с cat такой разделитель есть — пайп. Да и просто же выглядит довольно уродливо
А в чём проблема? Если операционки — это линуксы, а даты их релизов не отличаются на года, то держать под системой контроля версий единый набор конфигов для них — это обычно не проблема. Ну, допустим, будет ли вам невыносимо плохо от осознания того факта, что у вас среди конфигов лежит что-то под неустановленную программу?
Другое дело, что я не выбрал VCS по одной простой причине. Переезжая, я не люблю тащить за собой всё окружение, включая дефолты, которые могут отличаться между версиями софта или разными дистрибутивами. Интереснее переносить лишь тот его сабсет конфигов, который настраивался сознательно и здесь автоматизация выглядит намного удобнее
Всё верно, гарантии можно обеспечить и неязыковыми инструментами.
В том числе посредством приставления охранника с дубинкой к каждому пользователю, чтобы валидировать ввод. Однако, это кажется странным только при доведении до абсурдаА если вам, например, нужен эффективный инкремент знакового счётчика, в этом случае довольно осмысленно завязаться на тот факт, что на современном оборудовании его переполнение займёт десятилетия. Но если вы выждали десятилетия, или запустили программу на инопланетном оборудовании и таки переполнили счётчик, внезапно сделав программу не программой на C++, то... ну, вас в меньшей степени будет волновать вопрос того, написана ли программа на C++
И дополнение. Ваш вывод не совсем верен. Согласно стандарту, написана ли программа на C++, всё же, зависит не совсем от внешних факторов. Как только UB стало неизбежно, программа ретроспективно перестаёт быть программой на C++. Т.е. никогда ею и не была, независимо от внешних условий
Здесь, конечно, можно было бы порассуждать, что конкретно перестало быть программой на C++ — программа в рамках этого запуска, каждая копия этого исполняемого файла или вообще каждая функционально эквивалентная программа в мире, но вряд ли стандарт даст вам ответ на этот вопрос.
Программа-то больше и не программа, и стандарт на неё не распространяетсяЗамечу, что скамеру, убедившему бабушку следовать его инструкциям, вовсе и не требуется отключать никакие системы защиты в её телефоне
Давайте я переформулирую мысль из поста:
И под "чисто маркетинговый показатель" подразумевалось именно несоответствие одного другому, поскольку этот показатель не может отобразить плавность/комфорт игры в отрыве от частоты обработки физики, который не указан. И такие показатели как раз и принято называть "чисто маркетинговыми"
Вы же, прочитав текст, не вникая в его смысл, сформировали у себя в голове почти противоположный тезис "картинка в 200 фпс в играх не являются необходимым условием..." и оспариваете его, хотя он заявлен не был автором
Простите, а где вы увидели в тексте заявление о том, что эта разница незаметна? Там написано совсем не это
Я просто пытался сказать, что содержимое заголовка и текста абзаца не вполне соответствует бенчмарку (ибо дело не в согласовании
signed
/unsigned
, а конкретно вunsigned
, код с которым, предположительно, окажется медленнее, даже если везде будетunsigned
)Именно поэтому радиус наблюдаемой вселенной оценивается именно в ~46 миллиардов световых лет, а не в ~13.8
Только вот в современном мире почти везде используются two's-complement-числа, для которых это приведение типов — no-op. И, вероятно, бенчмарки показывают такую разницу не из-за разности типов, а из-за, собственно,
unsigned
, код вокруг которого, предположительно, хуже поддаётся оптимизации из-за допустимости его переполнения с точки зрения стандарта (в отличие отsigned
, переполнение которого не определено стандартом)Вы же в курсе, что докер есть в официальном репозитории вашего дистрибутива и ставится единственной командой, не требуя ничего из перечисленного по вашей ссылке?
Просто завязывайте с виндовыми привычками читать сайты разработчиков, которые вам подсовывают левые установщики и неофициальные репозитории
Я тоже люблю винду похейтить, однако... У меня винда ставится за несколько минут. Да, это не минута, и nvme у меня в ноуте по-быстрее будет, но, простите... А средний юзер действительно заметит оптимизацию скорости установки винды с пяти минут до одной, пока заваривает чай? Я уж молчу, что большинство юзеров будут ставить себе винду примерно ноль раз за всю жизнь
И действительно ли этот результат стоит потенциальных неприятных последствий переписывания логики установщика?
А вы http-коды храните в интах? Тогда у вас более серьёзные проблемы, чем обсуждаемая выше, до которой ещё дорасти нужно
Тем, что одно из условий положительно, а другое негативно. Главное, что оба выражены прямо так, как подразумевал программист. А вот стоит только взять
if (!vec.isEmpty())
— здесь уже проблемы, описанные мной выше. Имеем ввиду наличие элементов, спрашиваем про отсутствиеЛюбой из двух следующих вариантов я считаю предпочтительнее:
Ещё лучше было бы, предоставляй стандартные контейнеры сразу оба — и
empty
, иhas_value
На самом деле нельзя. У них семантика разная,
std::optional
семантически предполагает отсутствие значения, это фича для конечных пользователей. А дляstd::any
(как иstd::variant
) это вынужденная фича, необходимая для поддержания мув-семантики, а не для пользователей. Т.е. не предполагается, что пользователь будет пользоватьсяhas_value
/valueless_by_exception
как нормальным интерфейсом, ему это в норме и не нужно. А соответственно и неявный каст не нужен — это не часть нормального повседневного интерфейса, так что ответ на вопрос...Однозначно нет. Пользователь для
std::any
в норме не пользуется этим интерфейсом. Более того, в архитектуре даже худших среди кодовых баз, с которыми я работал, ещё не было столь серьёзных проблем, чтобы мне нужно было использовать "такой"std::any
А там, где он использовался не как архитектурный костыль — там у меня была гарантия не только на конкретный тип в нём, и на то, что он точно всегда имеет значение
Одинаково плохо читается и это:
и это:
Если в первом случае отрицание далеко от непосредственного контекста, то во втором оно не на том месте и отделено от него синтаксическим шумом. А теперь просто сравните с
Может, настало время добавить немного выразительности в интерфейсы своих типов?
Как альтернатива, когда типы трогать не хочется, воспользуйтесь древней технологией — переменной:
Не только в программировании, но и при изучении, обдумывании и чтении, мы обычно строим не просто некоторую ментальную модель, а часто стараемся приводить её к иерархическому виду. Например, можно вспомнить, что структурированные правила из учебников учатся и воспринимаются сильно легче, чем разрозненные факты или тем более исключения из них (вспомните, легко ли учились спряжения)
Казалось бы, к чему я вообще о какой-то иерархии в этом вопросе и где здесь иерархия? Всё просто — рано или поздно мозг программиста начнёт любой
if (something)
обобщает до до некоторой общей абстракции. Не думаю, что у неё есть название, но я бы её выразил какif ok
/if not ok
. Когда мозг их обобщил, становится очень просто читать такой код. И дело не в его длине, а в том, что все подобные конструкции мозг начинает маппить на то самое внутреннее обобщениеНо при этом, когда вы местами используете
if (opt.has_value())
, местамиif (ptr != nullptr)
, где-тоif (result != http::ok)
, где-то ещё что-то... Мозг просто хуже это делает — вместо одного обобщения он работает лишь с частными случаями всюду. А исключения и частные случаи мозг не очень любитНо почему мозг точно так же не может обобщить разные примеры выше?
Потому что ещё был упомянут альтернативный вариант проверки,
if (opt != std::nullopt)
. И это просто ужасный, кошмарнейший пример, связанный уже не с длиной кода. Эта строчка подразумевает, что внутри ветки мы работаем с happy path. И автор имеет ввиду положительный путь исполнения, и код пишет под положительную ветку исполнения. Но в условии при этом он спрашивает буквально "не является ли путь негативным". Изначальное семантически чисто положительное условие превратилось аж в две инверсии. Имеем ввиду положительное — спрашиваем дважды негативное. И, хотя формально две инверсии не меняют семантику на негативную, мозг, видя инверсии и негативизацию в условии... считает их чётность... и считает крайне плохо, и сбоит. Видит отрицания — думает про unhappy path. В первую очередь, конечно. Во вторую таки считает чётность, откатывает в своей ментальной модели то, что навыдумывать успел... Много избыточной ментальной работы, много кеш-миссов в головеИ это ещё не всё. Теперь комбинируем все перечисленные проблемы вместе и получаем ещё одну:
if (opt != std::nullopt)
иif (opt.has_value())
выражают одну мысль, но совершенно разным языком, который не обобщить до одной понятной концепции в своей ментальной модели. И вместо того, чтобы читать произвольную ветку какif (ok)
, ваш мозг будет вынужден каждый раз дополнительно разбираться, что значит второй аргумент в сравнении, к какому "виду" сравнений оно относится, считать чётность инверсий и перепроверять себя, забивая ментальный контекст этой мусорной работой и утомляя васЧем больше вы утомляетесь, тем больше ошибок совершите, разных и в разных местах
Какой вывод? Выражайте мысли прямо, чтобы ошибаться меньше. И
if (something)
— самый простой способ выразить мысль прямо. Автор будет меньше раздумывать, каким именно правильным способом это написать (правильный останется единственным, если вписать в стайлгайд и распространить на всю кодовую базу), а значит и с меньшей вероятностью совершит ошибку из-за семантического насыщения. А пользователь тоже будет читать привычный паттерн на автомате, снижая риски запутаться при виде всех этих таких похожих визуально, но таких семантически разных!= nullpt
,!= http::ok
,!= nullopt
. Ой, а в средний закралась ошибка. А вы её заметили?У него достаточно взаимопротиворечивых проверок, так что апелляция к нему сомнительна
Это иллюзия, проявившаяся из-за того, что вы прямо сейчас погружены в контекст проблемы, понимаете проблему и думаете, что вне этого контекста так же легко заметите проблему. Стоит вам только забыть контекст и спустя время вернуться к коду, увидев такие строчки...
...никаких несостыковок вы тут не заметите. И явность вам никак вообще не поможет. Потому что проблема не в отсутствии явности, а в том, что мозг крайне плохо обрабатывает в условиях инварианты, завязанные на инверсию
Лично мне же такой код ещё и усложняет чтение. Ведь для кода в голове обычно строится некоторая ментальная модель. И она строится не на конструкциях языка. В моём, примере, мозг поступает иначе — определяет зависимости между телом ветки и содержимым условия. Это необходимая для понимания сути кода низкоуровневая часть ментального процесса. И независимо от того, способны ли вы её пронаблюдать в своём мозгу сознательно, она на некотором уровне работы мозга отрабатывает
И если в исходном условии зависимость лишь одна —
SomeGlobalPointer
, возможных исходов в ментальной модели тоже два — либо у насSomeGlobalPointer
, либо у нас неSomeGlobalPointer
. Добавив же== nullptr
, мы привнесли в ментальную модель ещё как минимум одну сущность. И не важно, что это неизменная часть условияВ нашей ментальной модели теперь сущностей, от которых зависит исполнение тела, стало в два раза больше — две. Также и возможное число исходов (где-то в фоне считаемое мозгом) увеличилось до четырёх —
SomeGlobalPointer
, неSomeGlobalPointer
,nullptr
, неnullptr
Это лишь увеличит когнитивную нагрузку при том же семантическом наполнении кода. Вы будете уставать чуть быстрее. Уставая же, будете совершать новые ошибки. Решали одну конкретную частную проблему — добавили новых неочевидным для себя образом
Несколько ноутбуков, среди которых я выбирал несколько лет назад, были дороже в комплектации "без ОС", чем с Windows. Так что чисто технически, Windows зачастую имеет отрицательную стоимость для конечного потребителя
Думаю, вы можете попробовать запустить на нём waydroid
TL;DR: пайп позволяет визуально отделить вход от выхода и совершать меньше действий в процессе правки команды
В своём рассуждении, вы ловко выкинули из примеров команд
pattern
:А ведь чаще всего вы будете нажимать стрелку вверх и менять именно паттерн (или даже весь
grep
) при том, что имя файла будет оставаться фиксированным. Но каждый раз, нажимая стрелку вверх, ваш курсор будет оказываться в конце строки, т.е. рядом сfile
, и вам каждый раз нужно будет перепрыгивать его, чтобы добраться до паттерна, который будет за сессию работы меняться куда чаще. Лично мне лишние прыжки через имя файла удовольствия не доставляют, поэтому я сознательно используюcat
. Вы, конечно, можете опротестовать и предложить мне более эффективную (с точки зрения машины) альтернативу:Но. Стрелку легко перепутать, уничтожив файл. Во-первых, стрелки рядом на клавиатуре; во-вторых, даже будь они далеко — перепутать их всё ещё можно. Ещё здесь нет явного и заметного разделителя, который было бы удобно искать глазами — имя файла миксуется с командой; в случае с
cat
такой разделитель есть — пайп. Да и просто же выглядит довольно уродливоТак я и проверил — сразу привёл релевантную ссылку с меткой
wontfix
в своём исходном сообщении, более релевантных страниц в выдаче не вижуА что значит
-march=native
в контексте wasm? Кажется, даже разработчики в некотором недоуменииА в чём проблема? Если операционки — это линуксы, а даты их релизов не отличаются на года, то держать под системой контроля версий единый набор конфигов для них — это обычно не проблема. Ну, допустим, будет ли вам невыносимо плохо от осознания того факта, что у вас среди конфигов лежит что-то под неустановленную программу?
Другое дело, что я не выбрал VCS по одной простой причине. Переезжая, я не люблю тащить за собой всё окружение, включая дефолты, которые могут отличаться между версиями софта или разными дистрибутивами. Интереснее переносить лишь тот его сабсет конфигов, который настраивался сознательно и здесь автоматизация выглядит намного удобнее