Pull to refresh
2
0

User

Send message

Нет, в статье до сих пор написано:

Вернёмся к примеру с двумерным массивом a[2][4]. Результатом первого оператора индексирования a[0] будет указатель на int[4], а значит доступ может осуществляться только в интервале [0...3].

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

Результатом первого оператора индексирования a[0] будет отнюдь не указатель на int[4].

И далее здесь в комментариях есть даже ссылка на ответ ИИ по этому поводу.

Вот мне кажется, что текущие компиляторы для х86/х64 кинут литерал в сегмент данных и ссылка никогда не пропадёт.

В данном случае вам кажется совершенно правильно.

Но это стандартом для всех систем и оптимизаторов! не гарантируется.

Почему вы не проверили это своё утверждение и не предупредили, что, вполне возможно, это вам только кажется?

Тем более, что сейчас достаточно задать ИИ всего два вопроса:

Каково время жизни строкового литерала в программе на языке C++?

и

В каком пункте стандарта C++17 говорится об этом?

чтобы получить исчерпывающий ответ (я здесь использовал Gemini, он у меня — "дежурный" ИИ) и просто открыть указанные ИИ 2 пункта стандарта с cppreference.

Первый (по ссылке нужный фрагмент подсвечен светло-зелёным):

16 Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified. [ Note: The effect of attempting to modify a string literal is undefined.  — end note ]

и второй:

1 All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program ([basic.start.static][basic.start.term]).

Как видите, гарантируется.

У вас тут будет копирование фст внутрь лямбды,

Верно, следует захватить, применив std::forward:

template <template <typename...> typename F, typename... FST>
auto
bind_first(FST &&... fst) {
	return [... fst{std::forward<FST>(fst)}]<typename... LST>(LST &&... lst) {
		F<
			std::add_lvalue_reference_t<
				std::add_const_t<
					std::decay_t<FST>
				>
			>...,
			LST...
		>{}(fst..., std::forward<LST>(lst)...);
	};
}

а не мув, который вы вероятно ожидаете видя &&.

Нет, в суть ссылок я вник как следует уже давно.
Здесь просто не продумал этот момент.

Локальная переменная, это fst в шаблоне bind_first(FST &&... fst) , которая захватывается в лямбду (в предыдущем комментарии я опечатался про корутину).

А какого типа эта переменная?

И да, в вашем примере (если бы он был рабочим), это была бы строковая переменная.

Что значит, строковая переменная?
Какой у неё тип?

Я — к тому, что в данном конкретном случае:

	auto const sendOrderGoog{bind_first<SendOrder>("Goog")};

dangling reference не образуется, и последующее использование sendOrderGoog в примере безопасно.

Но в общем случае, проблема, естественно, имеется.

Просто тупые сишники не знают, что нельзя возвращать ссылку на локальную переменную. :сарказм:

А если "локальная переменная" сама является ссылкой?

Там, на самом деле, не локальная переменная, а параметр функции, который является ссылкой.

Будет ли ссылка на него ссылкой на локальную переменную, находящуюся в этой функции?

Кстати, сомневаюсь, что тупой в состоянии разобраться в C++.

"Задача со звёздочкой: найдите, что здесь не так"

Не удалось подумать, @eao197 уже подсказал.

Но в данном случае при показанном конкретном примере использования dangling reference не образуется.

Я, на самом деле, когда писал, сначала задумался, что туда вписать, решил сначала временно вписать туда &, сделать основное, а потом вернуться к вопросу.

Основное сделал, а вернуться к вопросу забыл.

template <template <typename...> typename F, typename... FST>
auto
bind_first(FST &&... fst) {
	return [=]<typename... LST>(LST &&... lst) {
		F<
			std::add_lvalue_reference_t<
				std::add_const_t<
					std::decay_t<FST>
				>
			>...,
			LST...
		>{}(fst..., std::forward<LST>(lst)...);
	};
}

Как-то так, в первом приближении, должно выглядеть.

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

Тогда для такой практики в коде нужны комментарии, составленные исходя из обозначенной проблемы.

Или времени и на это нет, и поэтому лучше отказываться от перегрузки функций?

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

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

А обсуждаемый подход её только усиливает.

Если не думать и не хотеть решить проблему, сохранив решение, то так оно и останется.

Вот мне же почему-то удаётся находить решения, несмотря на то, что я, года два, если не три, как подзабросил C++, и только сейчас постепенно возвращаюсь к нему.

Удаётся, потому что хочу решить проблему и пытаюсь думать в нужном направлении.

Какой бы вы предложили?

Конкретный не предложу, но — очевидно, что в языке не должно быть UB. Уже только одно это — огромный шаг вперёд.

Или, наоборот, предварительная проверка — это лишние сотни наносекунд, и вы проиграли тем, кто их не делает (а выражает все требования в типах и проверяет максимальное их число при компиляции, например).

В обсуждаемом случае речь была явно не про HFT.

Мы можем усложнять этот пример бесконечно, но смысл, опять же, я надеюсь, понятен: полагаться на внешние ограничения — не всегда возможно, либо не всегда остановит вас достаточно рано.

Я говорил о том, что авторы статьи не потрудились выдумку свою соотнести с реальностью.

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

С каким — таким? «Посмотреть предложенный собеседником код и найти там проблемы»?

Нет, с подходом "забуду здесь то", "забуду там это".

Проблемы с предложенным вами подходом потому, что он нелокален. Ну, вернее, это проблема не вашего подхода, а общей философии C++: например, если вы почитаете или послушаете гугловского Титуса Винтерса, топящего за что-то вроде «C++ design unit is an overload set» (а не функция или класс или модуль или…), и послушаете про его же кулстори про эволюцию гугловской кодовой базы, то увидите, что добрая часть проблем вылезает именно из-за этой нелокальности. Семантика, скрывающаяся за именем, оказывается размазана не по одному определению одной функции за этим именем, а по всему множеству всех функций с этим именем, потенциально в разных неймспейсах

Да, проблема имеется, но это лишь значит, что следует стараться её обходить и избегать.

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

Трейдерам следует программировать на C++ только в одном случае: если они, в первую очередь, программисты на C++, а трейдеры они — уже по совместительству.

В остальных случаях это похоже на попытку бриться топором.
Одна ошибка — и весьма серьёзные последствия не заставят себя ждать.

У трейдеров должен быть другой язык программирования.

Речь о другом: у предложенного механизма есть много негативных аспектов, поэтому говорить, что он эквивалентен решениям из раста (или из хаскеля — я так-то раст не люблю и хаскелист вообще), несколько нечестно.

Я и не говорил, что он эквивалентен.

Я говорил, что он в C++ — есть, а в статье неявно подразумевается, что — нет.

Как успехи с продумыванием кодовой базы на года вперёд?

Это — не панацея, но если этим не заниматься, будет значительно хуже.

Ну, да. Но в этом случае она вылезает тоже.

Если не думать, то проблему не решить в 100% случаев.
Если же думать, то процент начинает ощутимо отличаться от 100.

Я решил проблему, связанную с тем, что функции `sendOrder` — перегруженные, и поэтому механизм std::bind_first не работает.

Удалось отложить инстанцирование и, соответственно, разрешение перегрузки, которая теперь происходит не в момент вызова bind_first, а в момент вызова того, что вернула bind_first (добавился шаблон класса SendOrder и шаблон функции bind_first, и этот шаблон в качестве первого параметра принимает шаблон класса, а не тип или значение):

#include <cstdlib>
#include <iostream>
#include <type_traits>
#include <concepts>

template <typename... R>
auto sendOrder(R...) = delete;

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price)
{
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << std::endl;
}

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << std::endl;
}

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage,
               double stoploss) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << ' '
	          << stoploss << std::endl;
}

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage,
               double stoploss,
               double takeprofit) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << ' '
	          << stoploss << ' '
	          << takeprofit << std::endl;
}

template <typename T>
concept FloatOrDouble = std::same_as<T, double> || std::same_as<T, float>;

template <typename... Ts>
concept HasFloat = (std::same_as<Ts, float> || ...);

template <typename... Ts>
constexpr bool is_enabled() {
	return sizeof...(Ts) > 0 &&
	       (FloatOrDouble<Ts> && ...) &&
	       HasFloat<Ts...>;
}

template <typename... R>
std::enable_if_t<is_enabled<R...>()>
sendOrder(const char *symbol, bool buy, unsigned quantity, R... r) {
	return sendOrder(symbol, buy, quantity, static_cast<double>(r)...);
}

template <typename... R>
struct SendOrder {
	void operator ()(R &&... r) {
		sendOrder(std::forward<R>(r)...);
	}
};

template <template <typename...> typename F, typename... FST>
auto
bind_first(FST &&... fst) {
	return [&]<typename... LST>(LST &&... lst) {
		F<FST..., LST...>{}(std::forward<FST>(fst)..., std::forward<LST>(lst)...);
	};
}

int main() {
	sendOrder("DAL", false, 10u, 5.0);
	sendOrder("DAL", false, 10u, 4.0f);
	std::cout << '\n';
	sendOrder("DAL", false, 10u, 5.0, 3.0);
	sendOrder("DAL", false, 10u, 5.0, 2.0f);
	sendOrder("DAL", false, 10u, 4.0f, 3.0);
	sendOrder("DAL", false, 10u, 4.0f, 2.0f);
	std::cout << '\n';
	sendOrder("DAL", false, 10u, 5.0, 3.0, 7.0);
	sendOrder("DAL", false, 10u, 4.0f, 2.0f, 6.0f);
	sendOrder("DAL", false, 10u, 4.0f, 3.0, 6.0f);
	std::cout << '\n';
	sendOrder("DAL", false, 10u, 5.0, 3.0, 7.0, 9.0);
	sendOrder("DAL", false, 10u, 4.0f, 2.0f, 6.0f, 8.0f);
	sendOrder("DAL", false, 10u, 4.0f, 3.0, 6.0f, 9.0);
	sendOrder("DAL", false, 10u, 5.0, 2.0f, 7.0, 8.0f);

	std::cout << '\n';

	auto const sendOrderGoog{bind_first<SendOrder>("Goog")};

	sendOrderGoog(false, 10u, 5.0);
	sendOrderGoog(false, 10u, 4.0f);
	std::cout << '\n';
	sendOrderGoog(false, 10u, 5.0, 3.0);
	sendOrderGoog(false, 10u, 5.0, 2.0f);
	sendOrderGoog(false, 10u, 4.0f, 3.0);
	sendOrderGoog(false, 10u, 4.0f, 2.0f);
	std::cout << '\n';
	sendOrderGoog(false, 10u, 5.0, 3.0, 7.0);
	sendOrderGoog(false, 10u, 4.0f, 2.0f, 6.0f);
	sendOrderGoog(false, 10u, 4.0f, 3.0, 6.0f);
	std::cout << '\n';
	sendOrderGoog(false, 10u, 5.0, 3.0, 7.0, 9.0);
	sendOrderGoog(false, 10u, 4.0f, 2.0f, 6.0f, 8.0f);
	sendOrderGoog(false, 10u, 4.0f, 3.0, 6.0f, 9.0);
	sendOrderGoog(false, 10u, 5.0, 2.0f, 7.0, 8.0f);
#if 0
	sendOrder("DAL", false, 10u, 5);
	sendOrder("DAL", false, 10u, 5, 3.0);
	sendOrder("DAL", false, 10u, 5.0, 3);
	sendOrder("DAL", false, 10u, 4.0f, 3);
	sendOrder("DAL", false, 10u, 4.0f, 3, 6.1f);
	sendOrder("DAL", false, 10u, 5.0, 2.0f, 7, 8.0f);

	sendOrderGoog(false, 10u, 5);
	sendOrderGoog(false, 10u, 5, 3.0);
	sendOrderGoog(false, 10u, 5.0, 3);
	sendOrderGoog(false, 10u, 4.0f, 3);
	sendOrderGoog(false, 10u, 4.0f, 3, 6.0f);
	sendOrderGoog(false, 10u, 5.0, 2.0f, 7, 8.0f);
#endif
	return EXIT_SUCCESS;
}

При вызове bind_first необходимо явно указывать первый параметр шаблона.

Класс SendOrder — один на все перегрузки sendOrder.

Я не знаю ни одного человека, решившего задачу останова, и не знаю ни одного человека, который бы знал C++.

Эти данные — слишком скудные, чтобы делать выводы по данному вопросу.

Knight capital — это так, намёк на то, что при факапах в коде проверки и ограничители срабатывают не сразу, и даже их может оказаться недостаточно (они ж обанкротились и закрылись).

Так там не в ограничениях дело было.
Он же не пытались купить или продать сразу 2^32 акций.

Так в коде выше на биржу уходит тоже большое число относительно мелких (если хочется, можете вместо sendOrder читать splitAndExecuteOrder или чё-т такое, сути это не меняет).

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

И мне почему-то кажется, что долго такой брокер не "проживёт".

Тем более, что если купить без плеча на N денег акций, то потерять, в худшем случае, можно не более N денег.

А зашортив акций на N денег, потерять можно неограниченное количество денег. Например, если после шорта акция выросла в 15 раз в цене, потери составят 15 * N денег.

И реальные случаи такие уже были.

Или вы думаете, что если вам надо купить/продать 100500 акций, то вы их покупаете одним пакетом? Это плохая идея по куче причин.

Критикуя статью, в частности, банкротство при отправке на продажу 2^32 акций, я подробно описывал детали продажи такого количества акций, откуда должно быть понятно, что в базовых вещах, связанных с биржевой торговлей, я разбираюсь весьма неплохо.

Ожидать, что люди будут в продакшене писать и поддерживать такой код — несерьёзно.

Дело в том, что это — код, демонстрирующий идею и её работоспособность, а не код для production'а.

Не знаю, почему вы восприняли его как код для production'а.

Зы, кстати про delete в шаблонах не знал.

Это предполагается по "философии" C++.

Если что-то применимо к функции, то логично предположить, что это применимо и к методам, и к шаблонам функций/методов, а в некоторых случаях — и к лямбдам.

Это не всегда так, потому что могут быть препятствующие тому причины, но по "философии" C++ логично это предположить.

Никто не знает С++ (с)

Это — верно, но "философия" C++, а также внутреннее ощущение от свойств механизмов C++ часто выручает.

Именно внутренне ощущение от механизма = delete натолкнуло меня на мысль о применении его здесь.

Вот я скопипастил ваш код (и заодно добавил requires из соседнего поинта) и сделал вид, что он эволюционирует со временем, «забыв» поменять тип первого аргумента в удалённой перегрузке:

С таким подходом проблемы будут независимо от языка.

Сможете объяснить среднему трейдеру, который на C++ пишет постольку, поскольку вы ему даёте апишку для вашего инфра-кода, а у себя он там обмазывается q, матлабами и прочим?

Наверное, это не его забота, обеспечить приёмистость не точно соответствующих типов в предоставляемом API, — но только таких, которые разрешены проектировщиком API, а не любых, которые язык позволяет.

Есть предложенный механизм, и если хотеть добиться результата, этот механизм можно использовать для достижения этого самого результата.

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

Если не продумывать каждое решение, а пытаться наворачивать сверху, да побыстрее, а также быть вынужденным постоянно что-то менять, — да проблемы будут.

Да, а что? std::bind_front(&sendOrder, "GOOG");

Эта проблема не является специфичной для данного случая.
Придётся приводить к одной из перегрузок тем или иным способом или указывать явно параметр шаблона.

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

Почему? Я хочу, чтобы у меня в кэшлайн помещалось 16 цен, а не 8 — почему вы мне запрещаете так делать?

Я не запрещаю, это — просто наблюдение.

Не, просто задача «освоить C++» примерно настолько же реализуема, как «решить проблему останова».

Всё же, первая задача значительно проще, вы преувеличиваете.
Правда, это не значит, что первую задачу можно досконально решить.
Но досконально и не требуется.

Вовсе нет. Просто считать, что это число вот прям сразу пойдёт в пакетик на биржу — это несколько ошибочно.

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

И если вы хотите купить 10 акций, а случайно покупаете -10 = 4294967286, то до того, как у вас сработают всякие риск-чеки, дневные лимиты, и прочее, у вас уже уйдёт ордеров на сотню тыщ акций.

Нет, сработает проверка на максимальный объём в одной сделке, и вернётся ошибка, ничего никуда не уйдёт.

И если бы ушло, то ордер был бы один.

Ещё код по теме можно посмотреть в моём ответе @boldapeу здесь.

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

А вы сами — в курсе, что там произошло, и при чём здесь, вообще, C++?

И в наше время Google'ить такие вещи — неэффективно, особенно, если необходимо понять суть, а не поверить в чей-то пересказ через 10-ые руки.

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

Учебный он или нет — не важно.
Имеющуюся проблему не пытались решить по-настоящему.
Для этого не требуется знать C++ досконально.
И это — не какая-то редко используемая тонкость редко используемого механизма.

Отсылка к проблеме остонова означает оценку сложности такой "простой" задачи как "пойди уже и выучи наконец плюсы" как равную == задача невыполнима в принципе.

Это — несопоставимые по сложности задачи, C++ выучить значительно проще, тем более, что для решения данной задачи не требуется его учить досконально.

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

Пункт про обновление сигнатуры это когда вы решили добавить ещё один параметр и сделали это только в реальной функции, но забыли (или если джун, то даже и не предполагали что надо) обновить темплэйт делет, чтобы учесть новый параметр.

По поводу добавления параметров как для единственной функции, так и для перегруженных функций: можно применить parameter pack в шаблоне.

Примерно, так:

#include <cstdlib>
#include <iostream>

template <typename... R>
void sendOrder(R...) = delete;

void sendOrder(char const *symbol,
               bool buy,
               unsigned quantity,
               double price)
{
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << std::endl;
}

void sendOrder(char const *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage)
{
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << std::endl;
}

int main() {
	sendOrder("DAL", false, 10u, 5.0);
	sendOrder("DAL", false, 10u, 5.0, 3.0);
#if 0
	sendOrder("DAL", false, 10u, 5);
	sendOrder("DAL", false, 10u, 5.0, 3);
#endif
	return EXIT_SUCCESS;
}

Теперь единственный шаблон "обслуживает" все перегрузки функции sendOrder.
Его не требуется обновлять.

По поводу double или float: что значит "обмазываться"?

Применять?

Можно и применить, например:

#include <cstdlib>
#include <iostream>
#include <type_traits>
#include <concepts>

template <typename... R>
auto sendOrder(R...) = delete;

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price)
{
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << std::endl;
}

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << std::endl;
}

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage,
               double stoploss) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << ' '
	          << stoploss << std::endl;
}

void sendOrder(const char *symbol,
               bool buy,
               unsigned quantity,
               double price,
               double slippage,
               double stoploss,
               double takeprofit) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << ' '
	          << slippage << ' '
	          << stoploss << ' '
	          << takeprofit << std::endl;
}

template <typename T>
concept FloatOrDouble = std::same_as<T, double> || std::same_as<T, float>;

template <typename... Ts>
concept HasFloat = (std::same_as<Ts, float> || ...);

template <typename... Ts>
constexpr bool is_enabled() {
	return sizeof...(Ts) > 0 &&
	       (FloatOrDouble<Ts> && ...) &&
	       HasFloat<Ts...>;
}

template <typename... R>
std::enable_if_t<is_enabled<R...>()>
sendOrder(const char *symbol, bool buy, unsigned quantity, R... r) {
	return sendOrder(symbol, buy, quantity, static_cast<double>(r)...);
}

int main() {
	sendOrder("DAL", false, 10u, 5.0);
	sendOrder("DAL", false, 10u, 4.0f);
	std::cout << '\n';
	sendOrder("DAL", false, 10u, 5.0, 3.0);
	sendOrder("DAL", false, 10u, 5.0, 2.0f);
	sendOrder("DAL", false, 10u, 4.0f, 3.0);
	sendOrder("DAL", false, 10u, 4.0f, 2.0f);
	std::cout << '\n';
	sendOrder("DAL", false, 10u, 5.0, 3.0, 7.0);
	sendOrder("DAL", false, 10u, 4.0f, 2.0f, 6.0f);
	sendOrder("DAL", false, 10u, 4.0f, 3.0, 6.0f);
	std::cout << '\n';
	sendOrder("DAL", false, 10u, 5.0, 3.0, 7.0, 9.0);
	sendOrder("DAL", false, 10u, 4.0f, 2.0f, 6.0f, 8.0f);
	sendOrder("DAL", false, 10u, 4.0f, 3.0, 6.0f, 9.0);
	sendOrder("DAL", false, 10u, 5.0, 2.0f, 7.0, 8.0f);
#if 0
	sendOrder("DAL", false, 10u, 5);
	sendOrder("DAL", false, 10u, 5, 3.0);
	sendOrder("DAL", false, 10u, 5.0, 3);
	sendOrder("DAL", false, 10u, 4.0f, 3);
	sendOrder("DAL", false, 10u, 4.0f, 3, 6.0f);
	sendOrder("DAL", false, 10u, 5.0, 2.0f, 7, 8.0f);
#endif
	return EXIT_SUCCESS;
}

Один шаблон с parameter pack'ом, который "запрещает приведения".

Затем ещё один перегруженный шаблон с использованием SFINAE выступает обёрткой, но только для тех вызовов, в которых используется хотя бы один float.

Перегрузок много, а шаблон функции-обёртки — один.

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

Потому что вам теперь надо обновлять обе сигнатуры, и компилятор далеко не всегда напомнит вам это сделать.

Зачем их обновлять?

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

Потому что теперь вы не можете взять адрес sendOrder(по крайней мере, в контекстах вроде аргумента для стандартных алгоритмов, где ожидаемый тип callable не фиксирован).

Если имеются перегрузки, это и так невозможно без фиксации ожидаемого типа.
И это — правда, та функция, адрес которой, весьма вероятно, необходимо будет брать?

Потому что если вы захотите принимать double или floatпоследним аргументом

Сейчас там и так double последним аргументом.
В таких местах float не используется.

И решить проблему останова на сдачу.

В данной задаче это — необходимо?
По-моему, вы выдумываете несуществующие трудности.

Человек, отвечающий подробное, даже не понимает смысла учебных, иллюстрирующих примеров.

Не понял мысли.

Knight capital, кстати, передаёт привет.

Вы, видимо, обознались.

Но сначала он обращает внимание на количество (quantity) и стоимость (price), подчеркнув, что C++ сильно усложняет защиту вызывающей стороны от ошибок: компилятор допускает и 1000.00 в качестве quantity, и 100 в качестве price, не выдавая никаких предупреждений, несмотря на то, что они относятся к другим типам. Он просто выполняет преобразования.

Наверное, прежде чем "продавать" что-то сомнительное, следовало бы научиться пользоваться C++:

#include <cstdlib>
#include <iostream>

template <typename T1, typename T2, typename T3>
void sendOrder(const char *symbol, T1 buy, T2 quantity, T3 price) = delete;

void sendOrder(const char *symbol, bool buy, unsigned quantity, double price) {
	std::cout << symbol << ' '
	          << std::boolalpha << buy << ' '
	          << quantity << ' '
	          << price << std::endl;
}

int main() {
	sendOrder("DAL", false, 10u, 5.0);
#if 0
	sendOrder("DAL", 1, 10u, 5.0);
	sendOrder("DAL", false, 10, 5.0);
	sendOrder("DAL", false, 5.0, 10);
#endif	
	return EXIT_SUCCESS;
}

При попытке раскомментировать за'if'-0-енное, компилятор отказывается компилировать.

Почему не было даже попыток применить данное решение?

В результате вы продадите 4294967196 акций и обанкротитесь.

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

Не каждый эмитент имеет столько выпущенных акций.

Хорошо, пусть речь идёт о таком эмитенте, который выпустил столько или большее количество акций.

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

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

У подавляющего большинства трейдеров нет столько денег на депозите.

Хорошо, предположим, что и деньги есть, и у брокера есть необходимое количество акций.

Чтобы их продать, необходимо, чтобы их кто-то сразу купил, а для этого в биржевом "стакане" должны быть заранее выставлены лимитные заявки на покупку всего объёма продаваемых акций.

Очевидно, что это — весьма маловероятное событие.

Наконец, как правило у брокера в API есть ограничение на объём в одной торговой операции, которое значительно меньше 4294967196.

Но — зачем все эти тонкости, если можно просто ляпнуть о банкротстве?

Вместо всего этого, насколько я понимаю, была ссылка на авторитет.

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

Бесполезно продавать Rust, тем более, таким способом.

5-10 лет практики программирования имеет человек на выходе из универа, и его можно условно считать джуном (т.е. человеком который более менее усвоил теор. минимум https://sharpc.livejournal.com/67583.html).

Зачем требовать от джуна-программиста требования знания электроники, тем более, аналоговой, которая на порядки сложнее в понимании, чем цифровая?

Ладно электроники, зачем от него требовать знания автомата Калашникова?

Может, ещё потребовать теоретические знания, связанные с артиллерией, включая детали, связанные с физикой выстрела и полёта снаряда, и заканчивая знаниями особенностей современных видов пороха и прочих взрывчатых веществ как с точки зрения химии, так и с точки зрения физики?

Есть такие люди, которые заявляют на много порядков бо́льшие требования, чем они есть на самом деле, и делают вид, что это и есть истинное положение вещей.

Сеньор-программист, наверное, тогда должен знать всю геологию, всю космическую область, включая особенности, связанные с релятивистскими эффектами, а также атомные технологии, применяющиеся как в атомной промышленности, так и в военной сфере. Бонусом будет знание теоретической физики и физики ядерных частиц. Да?

Нет.

Простите, но это звучит наивно.

Мы здесь — о реальности или о чьём-то восприятии?

Вы действительно знали в 2009, что это дно?

Плюс-минус полгода-год — да.
А если на график смотреть, то можно и точнее.

И необязательно прямо самое дно ловить.
Результат, если поймать дно неточно, будет чуть похуже, но — явно лучше, чем войти прямо перед кризисом.

История рифмуется, большинство кризисов — циклические, примерные временны́е параметры таких кризисов известны, и они не отличаются в десятки раз.

Достаточно взглянуть на график, чтобы в этом убедиться.

Тогда вы принадлежите к немногим избранным.

Для этого не требуется инсайдерский доступ, всё есть на графиках.

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

Давайте заканчивать дискуссию.

Если вам кажется, что раз вы чего-то не знаете, то и никто не может знать, то — да, давайте заканчивать.

Более того, в стандарте сказано, что rand выдаёт значения от 0 до RAND_MAX. С чего вы взяли, что на платформе автора RAND_MAX не больше чем в три раза меньше INT_MAX?

Опять же, я — об общем случае, а не обо всех случаях.

Это уже казуистика пошла, но если так хотите: вы не показали, что в этом коде возникает переполнение инта.

Я не показал, что переполнение int'а возникает для любых случаев, зато показал, что возникает для одного конкретного.

Очевидно, что этот конкретный случай — не единственный, есть ещё и другие случаи, для которых возникает переполнение.

Во-первых стандарт не гарантирует случайность функции rand

Опять же, вы опровергаете не то, о чём я говорил.
Я не говорил, что проблема наличествует для всех случаев.

во-вторых автор мог заранее проверить, что при конкретном сиде не возникает переполнение

Ну, и что, что в каких-то конкретных случаях проблемы нет?

В общем-то случае проблема имеется.

Более того, некоторые LLM-ки это "видят" и даже пытаются "думать" в правильном направлении, хотя и с ошибками.

В частности по второй ссылке, которую привёл @izuck3n можно по строке "Integer overflow risk" обнаружить следующее:

Summing three rand() calls can overflow the range of int on platforms where RAND_MAX is near INT_MAX. Unsigned would wrap, signed is undefined behavior. A safer approach if you really want to combine calls is to do something like

unsigned long sum = (unsigned)rand() + (unsigned)rand() + (unsigned)rand();
RANDOM = sum / 3;

Предложение от ChatGPT — неверное, но "ход мысли", в целом, верный.

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

И проблема будет проявляться в очень существенном количестве случаев, навскидку — в половине случаев, поэтому "не заметить" её будет нельзя.

Иногда проще получить ответ brute-force'ом, что я и сделал.

Можно не дёргать каждый раз srand, а выполнить его один раз вначале или вообще не выполнять ни разу, — результат получается одинаковый.

Как видно, UB имеется в 83% случаев.
Игнорировать такой процент случаев — невозможно.

Вопрос, который был задан: "Что по мнению ИИ он делает, и зачем?".

И правильный ответ — делает что угодно, ибо — UB.
А зачем — можно только гадать.

Что конкретно в этом ответе не так?

На языке C имеем UB в связи с переполнением int.

Наблюдать можно здесь.
Average1 считается неверно для всех не-MSVC компиляторов.

Потому что библиотека использует для случайного числа весь доступный диапазон положительных значений типа int.

Ой ну какой вы прямо, ну дайте же мне похвастаться :)

Так вы и похвастались.

Смысл в том, что 45-летняя тётка с советским бэкграундом отважилась влезть в инвестирование, и все получилось.

Если купить индекс на много лет, то как тут может не получиться, даже если купить прямо перед кризисом?

Просто менее эффективно, чем могло бы быть, если купить сразу после кризиса.

Так что дочери автора, современной молодой девушке, сам бог велел!

А вы знаете, какой силы потрясение ожидает рынки в ближайшие месяц-два-три?

Инвестировать следует аккурат сразу после кризиса, а не до.
Иначе — неэффективно получается: несколько лет ждать придётся, пока последующий рост перекроет падение в кризис.
И только потом пойдёт доход.

Посмотрите курс SP500 в 2008-ом году и в 2009-ом.
Ориентировочно, 1400 и 870.
И вернулся к 1400 только к 2012-ому году.

Если его купить в 2008-ом по 1400, то в 0 выйти удастся только к 2012-ому.
4 года ожидания выхода из убытков, и только потом начинает идти прибыль.

А если купить на год позже, в 2009-ом, по 870, то через 3 года, к 2012-ому, прибыль составит 61%. Ведь есть же разница?

То есть, лучше год подождать, пока всё упадет до дна, и тогда и инвестировать.

Правда, нынешний кризис — не такой, как предыдущие, неизвестно, когда рост начнётся. Не один год может пройти, и не два. Да и рынки могут кардинально поменяться.

Поэтому я сомневаюсь, что кто-то там велел сейчас инвестировать.

1
23 ...

Information

Rating
3,850-th
Registered
Activity