Comments 64
Ага. И чтобы время компиляции просто так возросло на 10% (образно говоря).
А почему нельзя сделать так, чтобы компилятор всегда по умолчанию генерировал код с дефолтным поведением для операций сравнения?Тогда перестанут выявляться случайные ошибки/опечатки, когда программист пытается сравнить не сравнимые объекты. Можно, конечно, явно требовать запрета сравнения
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);
И компилятор её не выявил.
Правда, это не совсем парсинг плюсов, но я думаю, что 0xd34df00d имел в виду это.
Да, разумеется, не на стадии препроцессора. Спать надо больше :)
Но вообще, понятие парсинга языка неоднозначно. Давайте просто скажем, что от исходника до объектного модуля добраться — алгоритмически неразрешимая задача. И даже проверить, доберёмся ли.
Так что препроцессор — это, всё-таки, ещё не парсер. А фишка в том, что для большинства языков это если не простой LR(1) парсер, то что-нибудь недалеко от него ушедшее.
В случае же с C++ парсер обязан включать в себя интерпретатор приличного подмножества всего языка C++!
И туда вы можете любую задачу засунуть не только теоретически, но и практически — то есть это не тьюринговская трясина, а практически используемые вещи. Вплоть до того, что есть даже пропозал научить C++ читать ещё и внешние файлы.
Вот тогда совсем хорошо будет, когда ваша IDE будут ходить в prod за описанием базы данных…
Правда, за счёт того, что на прод ходит и описание схемы или БД генерирует один модуль, а пользоваться сгенерированным можно только из другого модуляНе, это читерство. Так и rust умеет. И думаю много кто ещё. А вот по настоящему, по хардкорному, чтобы если компилируют в 10000 потоков на сервере компиляции, то весь прод вставал бы раком — это только в C++. И то пока не включили в стандарт.
С другой стороны, технически и шаблоны C++ не Тьюринг-полны — стандарт позволяет иметь реализациям ограничения на глубину и количество инстанциирований, например.Ну эта проблема уже давно решена: вы можете просто завести
constexpr
функцию, внутри которой заводите себе массив на мегабайт (или гигабайт) и, спасибо C++14, его можно спокойно обрабатывать разными способами. Вполне себе бейсик такой — а на нём, в своё время, чего только не писали.Он возвращает только одно из трех значений? Как обстоят дела с "несравниваемостью"?
Например в D существует целая группа операторов сравнения, учитывающая возможность несравнимости, в частности для всяких NaN.
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 успеется, решить эту проблему.
Вполне можно представить себе кастомную функцию сравнения для сортировки, которая выполняет обычное сравнение строк, но не различает буквы Е и Ё.
Например, в зависимости от какого-нибудь флага в объекте сравнивать либо по одному полю, либо по другому. Пригодится, например, для std::string: если строка короткая, применено short string optimization и содержимое строки лежит в самом классе, иначе искать его надо по указателю.
struct data {
char* name;
};
Что будет обозначатьfunction __compare_sequence () {
return {this.name};
}
Сравнение указателей? Сравнение строк как null-terminated? А почему как null-terminated, а не фиксированной длины? А как перейти на сравнение указателей, если надо сравнивать так?В этом случае он не сможет работать в compile-time, т.к. содержимое строки ещё не известно.
Если же он возвращает алгоритм, который будет скомпилирован в такой цикл, то проще алгоритм написать сразу в обычном operator<=>
Для очень простого примера получается непростое решение.
Любой знак сравнения можно вывести из любого.
Это утверждение верно только для частичных строго упорядоченных множеств.
В C/C++, на самом деле тоже — но тут ограниченная проблема: разработчики IEEE 754 свинью подложили.
Плюсы все сложнее и сложнее…
А в чем усложнение? В том, что не надо будет, как сейчас, писать кучу member operator'ов и friend operator'ов на каждый чих? Это упрощение, а не усложнение.
Тут нет противоречия…
www.youtube.com/watch?v=ULkwKsag0Yk
Clang эту фичу добавил сильно позже, чем MSVC, уже в этом году… но зато у него два варианта сразу одинаковый год генерят, а у Microsoft когда до этого руки дойдут?
А что нужно в #ifdef ставить, чтобы знать, что «таки да — уже можно»? Проверять
__cpp_lib_three_way_comparison
нужно или только версию MSVC? А какая годится?Ну кто так, вообще, строит? Уж прогнать-то через компилятор код из рекламной статьи можно, а?
Новый оператор spaceship (космический корабль) в C++20