Обновить
2
0
Андрей Давыдов@AndDav

Пользователь

Отправить сообщение

У типобезопасности и метапрограммных трюков есть некоторая цена, как минимум в эталонной реализации от NVIDIA -- template instantiation stack почти переполняется даже при компиляции hello_world-а. Боюсь себе представить что будет, когда и если этим начнут пользоваться в реальном мире.

Разумеется, это совершенная ерунда. По открытым данным в 2021-м году в JetBrains работало 1900+ человек из них 1500+ в российских офисах. Если почти всех их уволить, продолжать разработку станет просто невозможно. Проще уж было бы компанию целиком закрыть.

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

Я склоняюсь к тому, что написать на автомате уже почти привычную всем конструкцию вида std::forward<decltype(self)>(self) и получить универсально работающий код проще, чем принимать решение о необходимости скопировать тело...

Написание на автомате приведет к тому, что вместе с const & и && будет объявляться еще и & перегрузка, а этого часто нужно избегать (нарушение инкапсуляции). Пример -- std::stringstream::str(). Так что нет, deducing this это не волшебная пилюля, позволяющая не думая получать корректный код.

В предложении рассмотрен такой пример. Он признан бессмысленным (такой метод в чём-то схож с operator void(), который никогда не вызывается) и бесполезным, поскольку он просто не может быть найден через unqualified name lookup.

Все так для почти всех методов, но я не просто так использовал operator ==: для него есть дополнительная стадия lookup (reversed operators), в которой резолвится y == x вместо x == y. В этой стадии мы, очевидно, заглянем в member-ы класса Y.

К чему я это все -- наивно думать, что добавление в C++ такой существенной новой вещи как "semi-static member functions" не приведет к никаким новым странным corner-cases. "This paper does not propose any changes to overload resolution" это намерение авторов, а что получится узнаем. spaceship operator тоже задумывался как не меняющий поведение никакого старого кода, а на практике случался миллион breaking changes.

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

А чтобы заставить код работать правильно, сейчас мы должны явно скопипастить тело функции как минимум 3 раза для const&& и && версий *this.,

Позволю себе слегка не согласиться -- если нужно 3, а не 2 перегрузки, т.е. мы хотим и & ссылку тоже предоставить, то обычно (не всегда) можно быть проще и сделать поле публичным вместо getter-ов.

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

Чтобы принять решение "вместо обычного нешаблонного getter-а мы будем писать шаблонную функцию с deducing this" все-таки надо задуматься, о том что в этом месте это полезно и пользы больше чем вреда, так что "автоматически" не получится. А если все равно задумываться, то и добавить &&-перегрузку большой проблемой не будет. Более того, с практической точки зрения, принятие "Deducing this" в C++23 никак не поможет починить существующие классы стандартной библиотеки, такие как std::filesystem::path потому что во-первых, они должны компилироваться в C++17-режиме, а во-вторых, ради ABI-stability нельзя отрефакторить non-template перегрузки в template.

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

По-моему, есть существенная разница с концептами -- последние не дублируют существовавшие до них языковые механизмы (не считая SFINAE-хаков вроде enable_if и void_t, по которым никто скучать не будет). Еще одно отличие от концептов, что в теории они могут использоваться и совсем в прикладном коде. Скажем, писать

std::vector<std::uint64_t>::const_iterator beg = myvec.begin();

кажется слишком длинным, а в записи

auto beg = myvec.begin();

кому-то не хватает информации о типе beg, в таком случае

Iterator auto beg = myvec.begin();

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

Непосредственно писать этот код нужно очень малой части C++ программистов, но он при этом косвенно затрагивает буквально каждого пользователя...

Не соглашусь, с тем что только косвенно. Explicit this это прямо новый способ писать member functions, позволяющий отказаться от function ref- and cv-qualifiers. Если бы он был в языке с самого начала, то и отдельный синтаксис для static methods был бы не нужен, и даже, можно пойти еще дальше, и ключевое слово this. В общем я предвижу глобальный раскол по стилю написания member functions (что, согласитесь, существенная часть кода на C++), который мне кажется куда серьезнее, чем войны между остро-/тупо- конечниками адептами trailing/non-trailing return type, east/west const и т.д.

При этом, очевидно, иметь в языке одну фичу (explicit this) лучше чем несколько (function ref- and cv-qualifiers, static methods, keyword this, ...) ну и бонусом те преимущества, что дает нам "Deducing this" proposal, так что если делать язык с 0, то надо идти именно по этому пути, как и сделали в Rust-е. Но добавлять explicit this сверху всего того, что уже есть в C++, по моему мнению принесет больше вреда, чем пользы.

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

struct X {};
struct Y { 
  bool operator == (this X, Y);
};

void test(X x, Y y) {
  x == y; // должен ли этот код компилироваться?
}

Я, как ясно из корневого комментария тоже отрицательно отношусь к этому proposal-у, но все-таки, давайте критиковать обоснованно.

  1. virtual functions с explicit this запрещены, так что неявный slicing нам не грозит.

  2. С указателями на функции вроде бы тоже никакой путаницы нет, в этом смысле функции с explicit this ведут себя как статические, для них же никого не напрягает, что указатели это не member-pointers.

В первом примере ошибка, такой код не скомпилируется. Должно быть

template<class Self>
decltype(auto) value(this Self&& self) {
  return forward<Self>(self).value_;
}

Или, на мой вкус чуть читаемей, так:

template<class Self>
decltype(auto) value(this Self&& self) {
    return forward_like<Self>(self.value_);
}

Вместо decltype(auto) тоже скорее всего можно написать явный тип, просто не зная что-такое value_ сложно сказать какой. Так что, если не записывать в одну строку, то все не так страшно.

А еще непосредственно в язык были добавлены auto(x) и "Deducing this". 2 фичи, нужные 0.1% пользователей, но усложняющие (добавляющие избыточность) в язык для всех. "Deducing this" в этом смысле, полный караул, auto(x) чуть менее страшно, просто auto теперь может встречаться еще в одном контексте.

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

Потому что последний шаблонный параметр может быть variadic.

Какую версию clang-а (и libc++) Apple на MacOS ставит мне не ведомо.

https://gcc.godbolt.org/z/q7vKrT
GCC, Clang и MSVC, релизные версии.

Просто современный C++ авторов статьи устарел уже лет на 10. У меня все помещается в строку:


template< std::ranges::bidirectional_range Range >
void quick_sort( Range & range ) {
    quick_sort( range, std::less_equal< std::ranges::range_value_t<Range> >() );
}

С тем что синтаксис C# пока что полегче я не спорю, и лямбды отличный тому пример. Но синтаксис корутины не усложняют.

Откуда Вы почерпнули информацию, что "Саттер уже подумывает о новом C++ NewLang на замену C++"?

А C# стал похожим на язык для пришельцев от добавления async/await, или асинхронный код написанный с async/await проще и понятнее, чем писали до этого (сколько там парадигм было 3 или 4, я не помню)?

Мне кажется, идею 3D-QuickHull можно изложить еще проще.


  1. Выбираем из исходного мн-ва 3 точки принадлежащие выпуклой оболочке. Результат — объединение выпуклых оболочек для подмножества точек лежащих выше и подмножества точек лежащих ниже плоскости, определяемой нашими 3-мя точками.
  2. Мы свели задачу к следующей. Есть 3 точки A, B, C не лежащие на одной прямой и мн-во точек S лежащих над плоскостью P проходящей через A, B, C. Построить оболочку для мн-ва S u {A, B, C}. Решение: выбираем из S наиболее удаленную от P точку D, она в ответ точно входит, разбиваем мн-во S\{D} на 4 подмн-ва:
    • (0) точки лежащие в тетраэдре (A, B, C, D) (они точно в ответ не попадут),
    • (1) точки над плоскостью (A, B, D),
    • (2) точки над плоскостью (B, C, D),
    • (3) точки над плоскостью (C, A, D).

Для мн-в (1), (2), (3) и соотвественно плоскостей (A, B, D), (B, C, D), (C, A, D) запускаемся рекурсивно.

По вашей ссылке на QuickHull удивительная статья, где геометрическая задача разбирается без единой картинки, это, по-моему, за гранью добра и зла. Если же нормально рассказывать идею алгоритма, используя только термины известные шестикласснику (без симплекса и детерминанта), то, как по мне, в 3D-случае QuickHull гораздо интуитивнее Джарвиса.

Достаточно одного функтора (в качестве компаратора подойдет std::equal_to<>), и его можно сделать однострочным:


struct string_hash : std::hash<std::string_view> {
    using is_transparent = std::true_type;
};

std::unordered_set<std::string, string_hash, std::equal_to<>>;

В общем-то мой комментарий был к тому, что "зная о такой возможности после прочтения заметки можно найти нужные примеры" немножко не правда, пока что именно про unordered containers качественных материалов нет, и даже самая свежая версия proposal-а P0919 отличается от того что в итоге попало в стандарт.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность