Как стать автором
Обновить

Комментарии 53

Обновлённая версия:

Клёво… не так ли? :)

Да не особо. Ошибся в порядке/кол-ве аргументов в constructArgs(...) и втихую получил nullptr, чтобы потом что? В рантайме на него проверять? Уж лучше static_assert втыкать

Да кто же ограничивает и мешает вставить static_assert в else.

так это же тавтология:
Пример:
template <typename Concrete, typename... Ts>
std::unique_ptr<Concrete> constructArgs(Ts&&... params) {
    static_assert(std::is_constructible_v<Concrete, Ts...>,
        "constructArgs(...) failed: Type is not constructible from args");
    return make_unique<Concrete>(forward<Ts>(params)...);
}

Здорово, целая статья про compile-time if! Ёжики, хватит жрать кактус (c++), переходите на тёмную сторону: в nim для этого есть просто ключевое слово when. Без всяких статей :)
Лучше переходите на brainfuck, у него одни плюсы (ну, и угловые скобочки с запятыми).
Спасибо за ультимативный аргумент! Теперь уж точно перейдем и все легаси перетащим! (нет)
Воу воу воу, полегче! Надеюсь, вы ещё не начали всё переписывать :) Смысл комментария был в другом: имхо невозможно усидеть на двух стульях сразу, что c++ и пытается делать — тут и совместимость сохранить надо, и фичи новые вводить. Получается монстр. Возможно, в один прекрасный момент вы захотите для вашего нового проекта попробовать что-то более продуктивное, с пакетным менеджером, с макросами, которые оперируют над AST (а не тупо подстановка), ну и с возможностью compile-time evaluation из коробки.
Тёмная — это потому что из-за тормозов приходится до поздна сидеть?
Я правильно понял, что if constexpr гарантирует что условие развернётся на этапе компиляции? С другой стороны как контролируется «разворачиваемость условия» во время компиляции.
Так и развернётся. Ложная ветка не будет компилироваться, примерно как #ifdef… #else… #endif.
Если не развернётся, будет выброшена ошибка компиляции, что нельзя на этапе компиляции это вычислить.
Если нельзя гарантировать — просто не скомпилируется. А про constexpr выражения в стандарте есть описание того, что им может быть — это и есть гарантия
Читать чужой плюсовый код станет еще более увлекательно.

Вопрос от дремучего непрофессионала. А зачем вообще нужно "шаблонное метопрограммтрование", и как часто всплывают кейсы, где без него никак? Вот именно никак, т.е. не решить задачу по другому. И пример, если можно) для общего образования.

Ну конечно можно решить, всё можно решить и тупо на асме или брэйнфаке, вообще на любом тьюринг полном языке. Вопрос в удобстве такие фичи как compile time if даёт богатый простор для оптимизации при этом упрощая синтаксис.
Я не умоляю устремлений людей придумывающих такие фичи, мне правда интересен кейс) Я смотрел лекции техносферы по С++, там упоминались такие вещи. Личной мой инструментарий С++ заканчивается на, скажем так, совсем базовой инкапсуляции.

мне правда интересен кейс
Самое простое это патерн матчинг для типов, различные конвертации и серилизации, работа с биндингами по типу буст питона, упрощение работы с контейнерами и ещё куча кейсов но более специфичных тип внедрения зависимостей, автоматов и тд.

Спасибо. Теперь есть от чего оттолкнуться для подробного гугления.

Очень много абстракций на таком уровне программирования, к сожалению нужно вникать, конечно все понятно, но на это уходит время и это печально)
Не знаю, но мое мнение человека с гуманитарным образованием, что если для скрипки варианта 17 нужно для одной ноты смычок обычной формы ( обычный if ) а для другой ноты смычок с бетонным подвесом ( if constexpr ), то возникает вопрос, а что на ней собственно такое можно сыграть, чтобы идти на такие жуткие мучения. Я конечно понимаю, что если бы это сделало путешествие на Луну из невозможного простым и обыденным. Тут как говорится будем грызть метал и constexpr. Но вот так, чтобы элементарно проверять тип данных из того же Reflection только под иным синтаксисом…

Я не того уровня специалист, чтобы указывать дорогу мастерам творящим С++. Так что извините, если мои сомнения выглядят как упражнения доморощенного Фомы не верующего, но мне кажется что что — то тут не совсем так.
Это не две разные ноты. Если в рамках вашей аналогии if — это смычок для ноты, то if constexpr — это ручка для рисования нот.
Вы хотите сравнить наше умение играть на скрипке или писать музыку?

Если я говорю, что это нота, то это нота. Пишите о том, что Вы знаете.

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

А вот если бы вы следовали тому, что проповедуете, то вы бы в треде про constexpr не отписывались.
Я прошу прощения, но фальшивые ноты в музыке программисту не грех заметить.

А повышенные тона программистов по поводу constexpr выдают то, на сколько это ошибочное нововведение. Нормальное «if» с таким огнём защищать не потребовалось бы.

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

Замеченное вами «повышение тона программистов» вызвано исключительно тем, что вы пытаетесь использовать неуместную аналогию. Оператор if constexpr не является смычком с бетонным подвесом, только и всего. И нет ничего удивительного в том, что на эмоциональный аргумент вы получаете эмоциональные ответы.
if constexpr и if в общем случае не являются взаимозаменяемыми. Это разные инструменты для разных задач. Не надо пожалуйста говорить что пианино — громоздкая гитара
«Не надо пожалуйста говорить что пианино — громоздкая гитара»
— Не говорил.
«Это разные инструменты для разных задач.»
— Как бы так это, хоть одним глазком в замочную скважину подглядеть, где этот новый инструмент блеснул своим «лаком» :)
Как бы так это, хоть одним глазком в замочную скважину подглядеть, где этот новый инструмент блеснул своим «лаком» :)

глядите сколько влезет
Судя по тому, что Вы предложили, с лаком у constexpr не очень.
в реализациях STL повсеместно используются всякие is_standard_layout/is_trivially_copyable/is_noexcept_movable/… Они позволяют выбирать наиболее эффективный алгоритм в зависимости от свойств типов шаблонов. Без if constexpr такие ветвления реализовать можно, но многократно большим объемом кода. Другой пример — асинхронный API с выбором реализации для функций, возвращающих T/void.

Для оценки полезности инструмента надо как минимум понимать его назначение. Подозреваю, у вас не было опыта написания более-менее сложного обобщенного кода.
«но многократно большим объемом кода»
— посмотрите дальше. Там есть разбор 3х примеров, из которых в 2х случаях сокращение объёма кода имело критически негативные последствия для его выразительности. То есть вместо понимания «языка» надо понимать скрытое понимание некоего секретного слова. Это называется не «опыт» а «цеховщина».

Дело не в том, что у меня мало опыта. Дело в том, что я вижу огромное давление на инженеров воспринять новые в прямом смысле слова выкрутасы, созданные неким заокеанским сообществом, не критично, не задавая вопросов и не осмысливая представленное. А работа их заокеанских коллег вызывает огромное количество более чем адекватных вопросов.
вы же не можете всерьез думать что диспатч через перегрузку шаблонной функции через enable_if более читаемый и внятный, чем через if constexpr?
Вот был бы он не const, а context, тогда делайте с ним что хотите. А так как он const, то он должен быть прост и постоянен. Функционал один, а слово другое.
Сходу так:
1
2
3/4
5

Всякие такие рефакторинги позволили сократить повторение кода и открыть дальнейшие возможности для его улучшения. Можно открыть текущую версию, сделать поиск по странице по if constexpr и прикинуть, как уродливо это выглядело бы в лучшем случае при SFINAE, в худшем — при необходимости дублирования кода. Например, в районе строк 980-1050.
Спасибо, очень дельный пример №1. Не знаю, может я становлюсь старым, но вот левый вариант мне нравится больше, чем правый. Он может и длиннее, но он как — то больше похож на рассказ на С++.
Пример №2 интересный. Буду учить, спасибо.
Пример №3 для меня был очень познавателен. Наверное, если так удобно, то как — то так работает. Но если не знаешь, и просто смотришь со стороны, то левая версия выглядит как будто в ней тоньше ухвачены детали. Нет?
Если позволите, огромное спасибо. Пример №2 меня заинтриговал.
эта и подобные фичи появляются в стандарте потому, что они нужны. И нужны они не вам. Возможно даже, что и не авторам библиотек, которыми вы пользуетесь. Скорее авторам какого-нибудь boost::python. Подумайте об этом в следующий раз когда будете писать что-нибудь типа import pyopencl.
Тут не только тип данных, метапрограмирование это довольно обширный класс задач, с помощью if constexp вы можете примерно в одном и том же контексте работать с совершенно другим типом при этом имея 0 оверхед (что никак не получится при runtime reflection) т.к все проверки происходят ещё на этапе компиляции.
0 оверхед очень дорогая вещь. Это имеет смысл для изменений уровня «полетим / не полетим на Луну». Подобные внедрения в код есть, например, в ядре обработки криптографических примитивов. Обычно их выполняют пользуясь расширением ассемблера asm {}.

Что касается templates и constraints, и далее метаданных и их проверки reflection, то Вы оцениваете оверхед через призму приземленного «а мне так кажется» пользователя, с целью оправдания сырого нововведения. Если это для «можно поспорить» точки зрения, то это конечно приемлемо. Но с точки зрения детального понимания runtime, то reflection так же неизбежен как смерть и налоги, без него просто нельзя. И в этом случае тривиальный запрос по верификации типа, это одна инструкция сравнения метаданных, которая в любом случае должны быть сделана. То есть для достаточно продвинутого компилятора это всего лишь вывод в стэк индикатора, считывающего результат целостности данных. То есть это 0 оверхэд в любом случае. Понимаете. Выигрыша нет. Зато язык стал сложнее. Вот в чем дело.

Может в этом нововведении есть какое — то зерно, которое мне не понятно. Вот об этом и был мой вопрос. Что — то о «полете на Луну». Именно это я и спросил. Если это есть, то пожалуйста, покажите.
И в этом случае тривиальный запрос по верификации типа, это одна инструкция сравнения метаданных, которая в любом случае должны быть сделана.

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

Вы серьёзно считаете что if constexpr делает код сложнее для чтения чем специализации или SFINAE? Вам наверно и лямбды не нравятся. С++ живой язык он развивается, и появляются конструкции которые сильно облегчают написание кода конечно при этом сам язык становится сложнее, потому что он даёт больше возможностей, но говорить что if constexpr не нужен, это всё равно что признаться что вы никогда не писали ничего сложного на шаблонах.
Зачем делать это сравнение если его можно сделать на этапе компиляции?


Дело не в этом, дело в том что Вы не совсем понимаете о чем идет речь. Учите матчасть.

Вы серьёзно считаете что if constexpr делает код сложнее для чтения


Усложнение кода не проблема сама по себе. Перечитайте ещё раз, я написал вполне доходчивую логичную фразу «Выигрыша нет. Зато язык стал сложнее.»

Насчет лямбда выражений, это очень интересная параллель, и я сам не решался её начать, поскольку она может открыть совершенно отдельную дискуссию, суть которой, как сделать расширение языка С++ в шаблоны лучше/правильно, а не сырым constexpr. Если позволите, она заслуживает отдельный ответ.
Дело не в этом, дело в том что Вы не совсем понимаете о чем идет речь. Учите матчасть.

Ну да, я тут извращаюсь с шаблонами и макросами чтобы впендюрить такую фичу только потому что не могу использовать if constexpr
succes = patern_match val
		with_cast(const std::vector<int>& items)
		{
			for (auto&& val : items)
				std::cout << val << ' ';
		}
		with_type(int x)
		{
			cout << "some int " << std::to_string(x) << '\n';
		}
		with_cast(const std::string& str)
		{
			cout << "some str " << str << '\n';
		}
		at_default
		{
			cout << "oops";
		};

А тут приходите вы и говорите что if constexpr не нужен и что мне вообще нужно учить матчасть. Ну спасибо вы мне прям глаза открыли.
Пока что, судя по диалогу с вами я могу лишь сделать вывод, что вы либо не достаточно компетентны (Эффект Даннинга Крюгера) либо троль, в любом случае общение с вами мне пользы не принесёт.
Но с точки зрения детального понимания runtime, то reflection так же неизбежен как смерть и налоги, без него просто нельзя.

Можете объяснить, почему?
Прошу прощения, это нельзя объяснить комментарием на форуме. Учите матчасть.
Ясно, спасибо.
Да бросьте Вы. Пробежался вчера по поисковику, чтобы сбросить что ни — будь похожее на эксплуатационные данные runtime с пояснением, почему там рефлекшн, и ничего не нашел. Нашел докторскую диссертацию 1972-го года американского профессора информатики ( computer science ) и несколько, не очень убедительных постов в различных форумах, типа stackoverflow.com. Никто не хочет связываться с объяснением что за чем и почему. И тем не менее, за исключением старых библиотек, которые никто не хочет трогать по разным причинам, ни одна из которых не является творческой, все современные runtime работает с рефлекшн. Поэтому к сожалению я вынужден последовать тренду всей компьютерной индустрии и утверждать по принципу «потому, что так делают все». Думаете мне это нравится… Интуитивно я могу попробовать собрать вместе аргументы «за». Но матчасть настолько многообразна, что всегда найдется ещё больше против. И тем не менее это факт. Если Вы возьмете индустрию в разрезе, то все операционные системы выпущенные в последние 5 лет имеют runtime, который обязательно выполняет интроспекцию и учет сущностей созданных тем или иным выполнимым. Не смотря на мой уклончивый ответ, он тем не менее, верен.
В Rust, насколько мне известно, нет рефлексии. В хаскеле рефлексию надо явно попросить для конкретных типов данных на этапе описания этих данных (Generic, Typeable и так далее). В C++17 (чем не современная реализация?) рантайм-рефлексии нет, не будет её и в C++20.

Но разговор хороший, конечно.
— Рефлексия нужна.
— Почему?
— Учите матчасть.
— Ясно.
— Ой, матчасть что-то не нашлась, но так все делают, поэтому я прав.
Рефлекшн сам по себе многолик. Он включает как интроспекцию и аудит, так и возможности манипулирвоания сущностями. Спор по теме ограничен функциями интроспекцией и аудитом. Эти функции обязательны даже для процесса «system» с 2013-го года. Их отключение возможно, но машина не пройдет сервис. И потом даже отключив эти функции, чтобы протестировать тот или иной модуль прошивки, отчет о запущенном выполнимом и сбор информации о сущностях, которые он создал, остановить нельзя. Это неотделимая часть runtime. Я не знаю если эти глубинные функции ОС реализованы на уровне аппаратной поддержки. Но если отталкиваться от тренда, то это похоже так.
Я не очень понял, что за процесс «system», что за модули прошивки, и так далее.

И, главное, я не очень понял, какое отношение конкретная предметная область имеет к дизайну языков.
Откройте регистр процессов и посмотрите, там процесс «system» хорошо видно. Прошивка это то, что по Английски называется firmware. То есть я пытаюсь пояснить, что если раньше инспекция сущностей были частью выбора пользователя, выбирающего тот или иной язык и, как Вы заметили, дезайн, ситуация изменилась и теперь рефлекшн, функции инспекции сущностей, это часть машины. То есть в нем нет оверхед. Есть просто языки, в которых исторически так сложилось, что к её возможностям нет доступа. В С++ они есть.
Откройте регистр процессов и посмотрите, там процесс «system» хорошо видно.

Не нашёл



Прошивка это то, что по Английски называется firmware.

Я, скажем так, не понял, какое это имеет отношение к интроспекции.

и теперь рефлекшн, функции инспекции сущностей, это часть машины. То есть в нем нет оверхед.

Вообще-то не часть машины и вообще-то есть.
Вот процесс в управляющем процессами.

К прошивке и корневому процессу отношение очень простое. Если раньше они не участвовали в интроспекции и это было частью выбора архитектуры, то с 2013-го года они сами ей подвергаются, с самого старта, и естественно, обеспечивают то же самое для всего, что только запускается на машине. Неужели не понятно?

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