И если бы я был интервьювером, то с моей точки зрения решение из двух строчек с std::forward_list::splice_after() и std::forward_list::sort() было бы самым правильным
Возможно, у того человека который проводил интервью у автора, это тоже было бы самым правильным. Проблема скорее всего в том, что автор не знаком со стандартной библиотекой в достаточной степени, чтобы ее применить в такой ситуации. Я думаю интервьюер был бы не против если бы автор предложил ему несколько решений с использованием стандартной библиотеки, просто чтобы выразить суть алгоритмов (например forward_list::sort + forward_list::sort + forward_list::merge или forward_list::splice_after + forward_list::sort), объяснил как это работает, что быстрее, какие ограничения накладывает односвязный список и попытался часть из этого реализовать. Начать можно было просто с сортировки пузырьком, а дальше предложить развитие, ни где же не требуется сложность изначально. Но, возможно, я ошибаюсь на счет интервьюера )
А с ним не нужно "работать", его нужно просто отсортировать (ну присоединить один к другому, а потом отсортировать), продемонстрировав знание элементарных алгоритмов, сложности, декомпозиции и т.д. и, например, решить задачу за O(n*log(n)), а не за O(n^2), порассуждать. Полноценная абстракция односвязного списка с шаблонами, аллокаторами, операциями, ни как не поможет в решение задачи, если человек не знает как отсортировать список, возможность вызвать метод insert вместо "ручного" присваивания одного указателя, не приблизит его к решению. Ну т.е. в чем смысл писать метод insert для задачи сортировки списка, если ты не знаешь как сортировать список... Напротив, это только покажет, что человек просто не понимает смысла задачи и самое главное зачем он ее решает. Все нужные операции, обобщения, оптимизации, баги ) можно добавить следующими итерациями, отталкиваясь от исходного решения, алгоритма.
Я встречал немного другой взгляд на требование C/C++. Все что перечислено в статье шаблоны, алгоритмы, итераторы, ренжи... - это все высокоуровневые абстракции. Помимо этого важно понимать, как из этих высокоуровневых абстракций в итоге получается машинный код. Как они реализованы и чего стоят. Возможно это кого-то удивит, но очень много программистов, у которых в резюме написан опыт 3+ лет на C++, не знают низкоуровневых вещей, например, многих ставит в тупик вопрос про выравнивание данных (здесь конечно можно заметить что "настоящему" программисту на С++, который пишет на "хорошем", "декларативном" С++ эти знания ни к чему - это тот же холивар, что и про алгоритмы). При этом я не встречал ни одного программиста на С, у которого подобные вопросы вызывали бы недоумение. Поэтому иногда когда пишут про C/C++ имеют ввиду знание как раз вот этого низкоуровневого наследия С, на котором построен C++. Но такой взгляд встречается редко, здесь вы правы скорее всего.
И во вторых, если в ЯЗЫКЕ нет массива, это не значит что РЕАЛИЗАЦИЯ(например от msvc) не сможет его сделать на built-in'ах, при этом ничего не потеряв вы убрали лишнюю вещь из языка.
Это также не значит что реализация СМОЖЕТ это сделать.
Но если предположить что смогла. Вот я могу взять clang и использовать стандартную библиотеку gcc или наоборот. По libc++, например, https://libcxx.llvm.org/#platform-and-compiler-support. Как это должно работать? Вы предлагает стандартизировать built-in'ы?
Никаким языковым способом не реализуемо на данный момент сделать их constexpr.
Это реализуется за счет union.
Заменить это всё можно одним фундаментальным типом byte и указателями, действительно: с помощью byte и системы типов С++ можно создавать любые типы, в том числе аналогичные int, double, float, bool и т.д. из фундаментального набора. Тут мы убиваем сразу несколько зайцев - нет больше исключений для фундаментальных типов в разрешении перегрузки, нет исключений в шаблонном коде для наследования( от фундаментальных типов нельзя наследоваться) ну и другие более мелкие исключения для подобных типов уходят в прошлое
И одним из этих убитых "зайцев" будет видимо constexpr.
Хорошо. Можете, пожалуйста, на https://godbolt.org привести код инициализации и использования вашего union_t в constexpr-контексте. Не важно каким компилятором и стандартом вы это сделаете?
Как массив байт с правильным алигментом, прямо в статье код делающий всё что вам нужно
И было бы здорово посмотреть, думаю не только мне, как вы с помощью массива байт реализуется ваш fucking::optional<T> как literal type, что требует стандарт.
Следующий код полностью заменяет юнион, не имеет никакого оверхеда относительно него и имеет более понятный пользователю интерфейс (emplace / destroy)
Как вы лихо запихали placement new и reinterpret_cast в constexpr-контекcт. Можете продемонстрировать как вы замените вот такой код с помощью вашего union_t?
struct T {
union {
int n;
float f;
};
constexpr T(int a) : n(a) {}
constexpr T(float b) : f(b) {}
};
int main() {
constepxr T a = 1;
constexpr T b = 0.5f;
static_assert(a.n == 1);
static_assert(b.f == 0.5f);
}
И реальный кейс: как вы планируете реализовать стандартный std::optional без union или std::variant?
Как же их заменить? Рекусивным(или через множественное наследование) туплом с элементами одного типа(да, это было очевидно)))
Если кому понравился мой корпус и статья, можете поставить мне лайк в Инстаграм
Корпус понравился, получилось здорово. У меня нет инстаграма, к счастью, перешел по ссылке посмотреть может там есть какие-то еще DIY-фото, но по итогу не очень понятно как ваш интаграм связан со статьей, плюс вы мне просто показываете фак и посылайте, прошу прощения, на х*й.
Инстаграм
Но возможно, я просто что-то не понимаю в маркетинге или как там это правильно называется.
Если вкратце, то компилятор имеет полное право удалить вызовы memset, если посчитает их бессмысленными (такое часто происходит, когда буфер очищается в конце операции и более не используется). Удостовериться в том, что компиляторы действительно могут убрать ненужный вызов, можно с помощью проверки аналогичного кода сервисом Compiler Explorer.
Приведенный фрагмент кода не аналогичен. Transmission использует Glib и g_free соответственно. Ни один компилятор не посчитает бессмысленым вызов memset и не уберет его перед вызовом g_free.
Хм, интересно, кажется clang ничего не выбирает вообще и не анализирует как используется переменная (правила формальной логики), анализатор говорит что это просто:
Branch condition evaluates to a garbage value
и дальше с этим ничего не делается, а просто считается false (имеет право, потому что UB). Судя по всему в версия 4.0.1 и еще в 5.0.0 с этим были баги
<source>:4:38: warning: Branch condition evaluates to a garbage value
int check_compiler() { int i; return i || !i; }
^
<source>:5:39: warning: Branch condition evaluates to a garbage value
int check_compiler1() { int i; return !i || i; }
^~
<source>:10:7: warning: Branch condition evaluates to a garbage value
if (x) // expected-warning{{Branch condition evaluates to a garbage value}}
^
<source>:17:10: warning: Branch condition evaluates to a garbage value
return x ? 1 : 0; // expected-warning{{Branch condition evaluates to a garbage value}}
^
4 warnings generated.
Вот, например, GCC генерирует код с одиночным if'ом таким образом, что мы прыгаем только если у нас ошибка (условие выполнено) https://godbolt.org/z/MET3vq
Мы можем явно сделать if "горячим" пометив его атрибутом [[likely]] и мы будем прыгать тогда когда ошибки нет (условие не выполнено), тогда год будет таким: https://godbolt.org/z/51c6oT
Можно попробовать вместо итерирования по индексам, получения элемента кортежа по индексу, преобразования его в вариант и последующем посещением варианта, сразу посещать кортеж. Например, как-нибудь, так:
Несложно добавить фильтрацию по индексам или другим критериям или генерировать отфильтрованный кортеж.
Как известно, кортеж представляет из себя гетерогенный список
Это не просто список, это список фиксированного размера, т.е. кортеж это обобщение std::pair, а не std::list. Т.е. непонятно зачем превращать кортеж и работать с ним как со списком std::variant. Можно тогда сразу использовать std::array<std::variant<...>, N>;
Еще вариант посмотреть на средства для алиасинга символов на стороне конкретного компилятора. Эти штуки пока не стандартизированы, но есть предложения, например, https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2729.htm
Но там есть свои нюансы. В С++ на GCC/Clang нужно будет указывать mangled имя https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html.
https://godbolt.org/z/sGPY3PdYM
Возможно, у того человека который проводил интервью у автора, это тоже было бы самым правильным. Проблема скорее всего в том, что автор не знаком со стандартной библиотекой в достаточной степени, чтобы ее применить в такой ситуации. Я думаю интервьюер был бы не против если бы автор предложил ему несколько решений с использованием стандартной библиотеки, просто чтобы выразить суть алгоритмов (например
forward_list::sort + forward_list::sort + forward_list::merge
илиforward_list::splice_after + forward_list::sort
), объяснил как это работает, что быстрее, какие ограничения накладывает односвязный список и попытался часть из этого реализовать. Начать можно было просто с сортировки пузырьком, а дальше предложить развитие, ни где же не требуется сложность изначально. Но, возможно, я ошибаюсь на счет интервьюера )А с ним не нужно "работать", его нужно просто отсортировать (ну присоединить один к другому, а потом отсортировать), продемонстрировав знание элементарных алгоритмов, сложности, декомпозиции и т.д. и, например, решить задачу за
O(n*log(n))
, а не заO(n^2)
, порассуждать. Полноценная абстракция односвязного списка с шаблонами, аллокаторами, операциями, ни как не поможет в решение задачи, если человек не знает как отсортировать список, возможность вызвать методinsert
вместо "ручного" присваивания одного указателя, не приблизит его к решению. Ну т.е. в чем смысл писать метод insert для задачи сортировки списка, если ты не знаешь как сортировать список... Напротив, это только покажет, что человек просто не понимает смысла задачи и самое главное зачем он ее решает. Все нужные операции, обобщения, оптимизации, баги ) можно добавить следующими итерациями, отталкиваясь от исходного решения, алгоритма.https://youtu.be/rHIkrotSwcc?t=1058
Я встречал немного другой взгляд на требование C/C++. Все что перечислено в статье
шаблоны, алгоритмы, итераторы, ренжи...
- это все высокоуровневые абстракции. Помимо этого важно понимать, как из этих высокоуровневых абстракций в итоге получается машинный код. Как они реализованы и чего стоят. Возможно это кого-то удивит, но очень много программистов, у которых в резюме написан опыт 3+ лет на C++, не знают низкоуровневых вещей, например, многих ставит в тупик вопрос про выравнивание данных (здесь конечно можно заметить что "настоящему" программисту на С++, который пишет на "хорошем", "декларативном" С++ эти знания ни к чему - это тот же холивар, что и про алгоритмы). При этом я не встречал ни одного программиста на С, у которого подобные вопросы вызывали бы недоумение. Поэтому иногда когда пишут про C/C++ имеют ввиду знание как раз вот этого низкоуровневого наследия С, на котором построен C++. Но такой взгляд встречается редко, здесь вы правы скорее всего.Хорошо, не нравиться cppinsight, давайте посмотрим на AST или трансляция кода это тоже не то, что реально происходит?
Вот для вашего примера
https://godbolt.org/z/3f1rMWG4e
А вот для обычного массива
https://godbolt.org/z/4T6hGoKnW
Это также не значит что реализация СМОЖЕТ это сделать.
Но если предположить что смогла. Вот я могу взять clang и использовать стандартную библиотеку gcc или наоборот. По libc++, например, https://libcxx.llvm.org/#platform-and-compiler-support. Как это должно работать? Вы предлагает стандартизировать built-in'ы?
https://eel.is/c++draft/expr.const#5
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1330r0.pdf
https://godbolt.org/z/zrcd6Mv18
Это реализуется за счет union.
И одним из этих убитых "зайцев" будет видимо constexpr.
Хорошо. Можете, пожалуйста, на https://godbolt.org привести код инициализации и использования вашего union_t в constexpr-контексте. Не важно каким компилятором и стандартом вы это сделаете?
И было бы здорово посмотреть, думаю не только мне, как вы с помощью массива байт реализуется ваш
fucking::optional<T>
какliteral type
, что требует стандарт.Как вы лихо запихали
placement new
иreinterpret_cast
в constexpr-контекcт. Можете продемонстрировать как вы замените вот такой код с помощью вашегоunion_t
?И реальный кейс: как вы планируете реализовать стандартный
std::optional
без union илиstd::variant
?Вы же это несерьезно, правда?
https://cppinsights.io/s/62550a23 и вот это у вас будет в каждом юните трансляции?
"Войнушка" - это когда дети во дворе друг за другом с палками бегают.
Вакансии
Видимо вы ищите талантливых "тыжпрограммистов".
К тяжелому?
Корпус понравился, получилось здорово. У меня нет инстаграма, к счастью, перешел по ссылке посмотреть может там есть какие-то еще DIY-фото, но по итогу не очень понятно как ваш интаграм связан со статьей, плюс вы мне просто показываете фак и посылайте, прошу прощения, на х*й.
Инстаграм
Но возможно, я просто что-то не понимаю в маркетинге или как там это правильно называется.
Приведенный фрагмент кода не аналогичен. Transmission использует Glib и
g_free
соответственно. Ни один компилятор не посчитает бессмысленым вызовmemset
и не уберет его перед вызовомg_free
.code
output
output
Хм, интересно, кажется clang ничего не выбирает вообще и не анализирует как используется переменная (правила формальной логики), анализатор говорит что это просто:
и дальше с этим ничего не делается, а просто считается false (имеет право, потому что UB). Судя по всему в версия 4.0.1 и еще в 5.0.0 с этим были баги
https://godbolt.org/z/EPd37cjfW
Я бы еще предложим немного расширить интерфейс обработки ошибок
Accept
'а (для консистентности).Сейчас чтобы пользоваться функцией нужно:
Как-нибудь так:
В самом предложении http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html есть несколько тестов
Вот, например, GCC генерирует код с одиночным if'ом таким образом, что мы прыгаем только если у нас ошибка (условие выполнено)
https://godbolt.org/z/MET3vq
Мы можем явно сделать if "горячим" пометив его атрибутом
[[likely]]
и мы будем прыгать тогда когда ошибки нет (условие не выполнено), тогда год будет таким:https://godbolt.org/z/51c6oT
Можно попробовать вместо итерирования по индексам, получения элемента кортежа по индексу, преобразования его в вариант и последующем посещением варианта, сразу посещать кортеж. Например, как-нибудь, так:
Несложно добавить фильтрацию по индексам или другим критериям или генерировать отфильтрованный кортеж.
Это не просто список, это список фиксированного размера, т.е. кортеж это обобщение
std::pair
, а неstd::list
. Т.е. непонятно зачем превращать кортеж и работать с ним как со спискомstd::variant
. Можно тогда сразу использоватьstd::array<std::variant<...>, N>
;