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

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

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

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

Ну, естественно нужно учитывать то, что используются эти операции или нет.
НЛО прилетело и опубликовало эту надпись здесь
Ждем в C++23
А почему нельзя сделать так, чтобы компилятор всегда по умолчанию генерировал код с дефолтным поведением для операций сравнения?
Тогда перестанут выявляться случайные ошибки/опечатки, когда программист пытается сравнить не сравнимые объекты. Можно, конечно, явно требовать запрета сравнения
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-й заход))
Чем дальше улучшают язык с++, тем больше он становится языком для машин, а не для людей. К моему большому сожалению, текст программ на современных плюсах все сложнее читать.
поверьте, сложность парсинга с++ увеличивается быстрее, чем сложность его чтения )
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Шаблоны, которые разбираются на этапе препроцессора, формируют Тьюринг-полный язык, а значит, для произвольной программы нельзя (алгоритмически) проверить, скомпилируется она когда-нибудь, или нет.
Правда, это не совсем парсинг плюсов, но я думаю, что 0xd34df00d имел в виду это.
НЛО прилетело и опубликовало эту надпись здесь

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

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

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

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

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

Вот тогда совсем хорошо будет, когда ваша IDE будут ходить в prod за описанием базы данных…
НЛО прилетело и опубликовало эту надпись здесь
Правда, за счёт того, что на прод ходит и описание схемы или БД генерирует один модуль, а пользоваться сгенерированным можно только из другого модуля
Не, это читерство. Так и rust умеет. И думаю много кто ещё. А вот по настоящему, по хардкорному, чтобы если компилируют в 10000 потоков на сервере компиляции, то весь прод вставал бы раком — это только в C++. И то пока не включили в стандарт.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
С другой стороны, технически и шаблоны C++ не Тьюринг-полны — стандарт позволяет иметь реализациям ограничения на глубину и количество инстанциирований, например.
Ну эта проблема уже давно решена: вы можете просто завести constexpr функцию, внутри которой заводите себе массив на мегабайт (или гигабайт) и, спасибо C++14, его можно спокойно обрабатывать разными способами. Вполне себе бейсик такой — а на нём, в своё время, чего только не писали.
НЛО прилетело и опубликовало эту надпись здесь
Может, конечно. Но тут уже начинаются проблемы с компилированием вполне легального кода. Стандартного значение -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) тоже "просто заработало") пока не обещают?

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

Это утверждение верно только для частичных строго упорядоченных множеств.
НЛО прилетело и опубликовало эту надпись здесь
И, тем не менее, в Javascript и PHP все операторы сравнения — именно такие.

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

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

НЛО прилетело и опубликовало эту надпись здесь

Плюсы все сложнее и сложнее…

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

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

Тут нет противоречия…
опять же, вопрос трактования. Что такое «сложность языка»? Число именованных сущностей? Букв в стандарте? Строк кода в компиляторе? На мой взгляд, за «простоту» языка стоит принимать именно выразительность/читаемость. А она с новыми стандартами только растет.
НЛО прилетело и опубликовало эту надпись здесь
У Саттера была очень понятная лекция про этот оператор. Гораздо понятнее, чем эта статья.
www.youtube.com/watch?v=ULkwKsag0Yk
Кстати — никто не знает: Microsoft вообще собирается когда-нибудь сделать так, чтобы фича не только была, но и чтобы её ещё, как бы, можно было бы и пользовать, а?

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

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

Ну кто так, вообще, строит? Уж прогнать-то через компилятор код из рекламной статьи можно, а?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий