Pull to refresh

Comments 64

А почему нельзя сделать так, чтобы компилятор всегда по умолчанию генерировал код с дефолтным поведением для операций сравнения? Тогда строчка auto operator<=>(const Basics&) const = default; ненужна. А если нужно все-таки, чтобы сравнения были «нестандартными», то тогда и переопределять операции. Или в этом есть потаённый смысл?

Ага. И чтобы время компиляции просто так возросло на 10% (образно говоря).

Ну, естественно нужно учитывать то, что используются эти операции или нет.
UFO just landed and posted this here
А почему нельзя сделать так, чтобы компилятор всегда по умолчанию генерировал код с дефолтным поведением для операций сравнения?
Тогда перестанут выявляться случайные ошибки/опечатки, когда программист пытается сравнить не сравнимые объекты. Можно, конечно, явно требовать запрета сравнения
auto operator<=>(const Basics&) const = delete;
Но это ломает совместимость.
когда программист пытается сравнить не сравнимые объекты.

Этот момент я не совсем понял. Т.е. компилятор не сможет понять, что в аргумент функции передан другой тип? Или проблема заключается в том, что класс-наследник может иметь отличные операции сравнения?
Проблема в том, что для многих объектов несколько бессмысленно сравнивать их лексикографически. Какое-нибудь «окно», например. Или комплексные числа.
Нельзя сравнивать объекты, которые специально не отмечены как сравнимые.
Например, программист сделал опечатку
Player p1, p2;
if (p1 > p2) Greetings(p1); else Greetings(p2);
вместо
if (p1.score > p2.score) Greetings(p1); else Greetings(p2);

И компилятор её не выявил.
А можно использовать operato<=> без default? А то пока выглядит не очень. Получается, он будет выводить операторы сравнения для всех полей в классе
Разумеется, можно.
То есть, определить свои правила сравнения?
Да, разумеется, правила перегрузки как и для прочих операторов, см.
Уже третий заход делаю на <=> и никак не пойму как он работает, что возвращает, и вообще как он сам формируется… Будет 4-й заход))
Чем дальше улучшают язык с++, тем больше он становится языком для машин, а не для людей. К моему большому сожалению, текст программ на современных плюсах все сложнее читать.
поверьте, сложность парсинга с++ увеличивается быстрее, чем сложность его чтения )
UFO just landed and posted this here
UFO just landed and posted this here
Шаблоны, которые разбираются на этапе препроцессора, формируют Тьюринг-полный язык, а значит, для произвольной программы нельзя (алгоритмически) проверить, скомпилируется она когда-нибудь, или нет.
Правда, это не совсем парсинг плюсов, но я думаю, что 0xd34df00d имел в виду это.
UFO just landed and posted this here

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

Ну вообще Wikipedia хотя и добавляет слова «обычно», но в целом согласна, что Результатом обычно является дерево разбора (синтаксическое дерево).

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

В случае же с C++ парсер обязан включать в себя интерпретатор приличного подмножества всего языка C++!

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

Вот тогда совсем хорошо будет, когда ваша IDE будут ходить в prod за описанием базы данных…
UFO just landed and posted this here
Правда, за счёт того, что на прод ходит и описание схемы или БД генерирует один модуль, а пользоваться сгенерированным можно только из другого модуля
Не, это читерство. Так и rust умеет. И думаю много кто ещё. А вот по настоящему, по хардкорному, чтобы если компилируют в 10000 потоков на сервере компиляции, то весь прод вставал бы раком — это только в C++. И то пока не включили в стандарт.
UFO just landed and posted this here
UFO just landed and posted this here
С другой стороны, технически и шаблоны C++ не Тьюринг-полны — стандарт позволяет иметь реализациям ограничения на глубину и количество инстанциирований, например.
Ну эта проблема уже давно решена: вы можете просто завести constexpr функцию, внутри которой заводите себе массив на мегабайт (или гигабайт) и, спасибо C++14, его можно спокойно обрабатывать разными способами. Вполне себе бейсик такой — а на нём, в своё время, чего только не писали.
UFO just landed and posted this here
Может, конечно. Но тут уже начинаются проблемы с компилированием вполне легального кода. Стандартного значение -fconstexpr-steps=1048576 достаточно, чтобы компиляция занимала часы.

Он возвращает только одно из трех значений? Как обстоят дела с "несравниваемостью"?
Например в D существует целая группа операторов сравнения, учитывающая возможность несравнимости, в частности для всяких NaN.

На cppreference описаны 5 типов, которые может возвращать operator <=>. Один из них (std::partial_ordering) как раз учитывает возможность несравнимости.
помимо этого, возможен некоторый буст производительности для алгоритмов. Например, set при лукапе будет использовать эквивалентность вместо равенства (a эквивалентен b если !(a < b) && !(b < a)), что не очень эффективно, т.к. строки сравниваются дважды даже если достаточно одного strcmp. С operator <=> некоторые алгоритмы сортировок/бинарного поиска можно оптимизировать.
newtype IntWrapper = IntWrapper Int deriving (Eq, Ord, Show)

Я смотрю, deriving Ord спустя пару десятков лет в мэйнстрим завезли. deriving Show (чтоб cout << IntWrapper(42) тоже "просто заработало") пока не обещают?

UFO just landed and posted this here
UFO just landed and posted this here
Далеко не всё можно так вывести.
Пример гарантированно эффективно раскладывающегося в железо кода
/* MAC address. */
#pragma pack(push, 1)
typedef struct lnk_mac_t
{
	union
	{
		uint8_t mac[6];
		uint32_t _f32tv;
		uint16_t _f16tv[3];
	};
#if defined(__cplusplus)
	inline const uint32_t& _first_part_get() const
	{
		return _f32tv;
	}
	inline const uint16_t& _second_part_get() const
	{
		return _f16tv[2];
	}
	inline operator const uint8_t* () const
	{
		return static_cast<const uint8_t*>(mac);
	}
	inline bool operator < (const lnk_mac_t& p) const
	{
		if (_first_part_get() < p._first_part_get())
			return true;

		if (_second_part_get() < p._second_part_get())
			return true;

		return false;
	}
	inline bool operator > (const lnk_mac_t& p) const
	{
		if (_first_part_get() > p._first_part_get())
			return true;

		if (_second_part_get() > p._second_part_get())
			return true;

		return false;
	}
	inline bool operator == (const lnk_mac_t& p) const
	{
		if (_first_part_get() != p._first_part_get())
			return false;

		if (_second_part_get() != p._second_part_get())
			return false;

		return true;
	}
	inline bool operator != (const lnk_mac_t& p) const
	{
		if (_first_part_get() != p._first_part_get())
			return true;

		if (_second_part_get() != p._second_part_get())
			return true;

		return false;
	}
	inline bool isIndividual() const
	{
		return ((mac[0] &
#if __BYTE_ORDER == __LITTLE_ENDIAN
			0x01
#elif __BYTE_ORDER == __BIG_ENDIAN
			0x80
#else
# error	"Please fix"
#endif
			) == 0); // See RFC 2469 for details
	}
	inline bool isGroup() const
	{
		return !isIndividual();
	}
	/* TODO CTP
	inline bool isCTP() const
	{
		// See https://en.wikipedia.org/wiki/Ethernet_Configuration_Testing_Protocol 
		// See https://aminems.github.io/ctp.html
		// See https://habr.com/ru/post/129399/
		// cf:00:00:00:00:00
		if (_first_part_get() != 0x000000cf)
			return false;

		if (_second_part_get() != 0x0000)
			return false;

		return true;
	}
	*/
	
#endif // defined(__cplusplus)
} lnk_mac_t;
#pragma pack(pop)


Если для примера или любого кода где оператор сравнения делает последовательно несколько шагов (в статье описан пример с std::string) начать из оператора less выводить остальные то получится плохо, особенно для операторов == и !=. Оператор <=> призван в перспективе, я надеюсь что в 20 успеется, решить эту проблему.
UFO just landed and posted this here
Да, очень хорошее предложение. Именно явно указанная последовательность членов если это необходимо и использование, например, ключевого слова auto чтобы компилятор сам эту последовательность выстроил.
Нельзя всё сводить к битовым строкам.
Вполне можно представить себе кастомную функцию сравнения для сортировки, которая выполняет обычное сравнение строк, но не различает буквы Е и Ё.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Вы начали с «магического метода». Какие в нём разрешены операторы — всё множество языка или ограниченное подмножество? Он выполняется в runtime или compile-time?
UFO just landed and posted this here
Если compile-time, процесс сравнения не сможет зависеть от данных.
Например, в зависимости от какого-нибудь флага в объекте сравнивать либо по одному полю, либо по другому. Пригодится, например, для std::string: если строка короткая, применено short string optimization и содержимое строки лежит в самом классе, иначе искать его надо по указателю.
UFO just landed and posted this here
Допустим, у меня такой класс:
struct data {
    char* name;
};
Что будет обозначать
function __compare_sequence () {
    return {this.name};
}
Сравнение указателей? Сравнение строк как null-terminated? А почему как null-terminated, а не фиксированной длины? А как перейти на сравнение указателей, если надо сравнивать так?
UFO just landed and posted this here
Этот for находится внутри __compare_sequence?
В этом случае он не сможет работать в compile-time, т.к. содержимое строки ещё не известно.
Если же он возвращает алгоритм, который будет скомпилирован в такой цикл, то проще алгоритм написать сразу в обычном operator<=>
Для очень простого примера получается непростое решение.
UFO just landed and posted this here
Любой знак сравнения можно вывести из любого.

Это утверждение верно только для частичных строго упорядоченных множеств.
UFO just landed and posted this here
И, тем не менее, в Javascript и PHP все операторы сравнения — именно такие.

В C/C++, на самом деле тоже — но тут ограниченная проблема: разработчики IEEE 754 свинью подложили.
У меня было такое, когда пришлось сравнивать диапазоны на числовой прямой: пример типичной операции сравнения «A <= B»: существует число, принадлежащее диапазону A, такое, что все числа принадлежащие диапазону B не меньше его. В этом случае операции равенства и неравенства диапазонов не выводятся из операций сравнения. Да и вообще, операции сравнения в этом случае ни коммутативны, ни антикоммутативны, т.е. нарушается базовое условие из определения частичных упорядоченных множеств. Хотя сравнивать в прикладном смысле можно.
UFO just landed and posted this here
можно узнать, о чем была программа?

Один из модулей ui, отвечавший за отрисовку диаграммы Гантта

UFO just landed and posted this here

А в чем усложнение? В том, что не надо будет, как сейчас, писать кучу member operator'ов и friend operator'ов на каждый чих? Это упрощение, а не усложнение.

Вы как бы оба правы. Язык — становится сложнее. Программы — на нём написанные — проще.

Тут нет противоречия…
опять же, вопрос трактования. Что такое «сложность языка»? Число именованных сущностей? Букв в стандарте? Строк кода в компиляторе? На мой взгляд, за «простоту» языка стоит принимать именно выразительность/читаемость. А она с новыми стандартами только растет.
UFO just landed and posted this here
Кстати — никто не знает: Microsoft вообще собирается когда-нибудь сделать так, чтобы фича не только была, но и чтобы её ещё, как бы, можно было бы и пользовать, а?

Clang эту фичу добавил сильно позже, чем MSVC, уже в этом году… но зато у него два варианта сразу одинаковый год генерят, а у Microsoft когда до этого руки дойдут?

А что нужно в #ifdef ставить, чтобы знать, что «таки да — уже можно»? Проверять __cpp_lib_three_way_comparison нужно или только версию MSVC? А какая годится?

Ну кто так, вообще, строит? Уж прогнать-то через компилятор код из рекламной статьи можно, а?
Sign up to leave a comment.