Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 08.02.23 10:17
Оценка: +3
Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://www.cppstories.com/2018/05/tmplbook2nd-review/
Вот что немного затрудняет продвижение, так это многостраничные объяснения как сделать одно и то же в 11, 14 и 17 плюсах. Отсюда вопрос, на ваш взгляд, имеет ли смысл сейчас запариваться с предыдущими стандартами? Такое ощущение, что через год С++20 уже будет повсеместно, может имеет смысл сразу его осваивать? Там, насколько я понял, куча всяких нововведений типа концептов, которые еще сильнее могут упростить код.

Потом еще один момент. Стандартная библиотека предлагает кучу утилитных методов для метапрограммирования. Часть из них, что-то типа std::is_class можно реализовать, например, используя SFINAE

template<typename T>
class is_class
{
    typedef char yes[1];
    typedef char no[2];
 
    template<typename C>
    static yes& test(int C::*); // selected if C is a class type
 
    template<typename C>
    static no& test(...);       // selected otherwise
public:
    static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};


Другие методы, по моим ощущениям (например std::result_of/std::invoke_result или noexcept operator), можно реализовать только на уровне компилятора. Отсюда вопрос, надо ли пытаться понять как это все внутри работает или подобные утилиты для метапрограммирования из стандартной библиотеки просто принять как данность?

UPD.
Забыл про философский вопрос Короче заметил, что плюсовое метапрограммирование представляют для меня пока просто какой-то хаотичный набор приёмов, методов, синтаксиса, а какая-то цельная картина происходящего не складывается. Я подумал, может это из-за недостаточной теоретической базы? Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?
Errare humanum est
Отредактировано 08.02.2023 10:30 Максим . Предыдущая версия .
Re: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 08.02.23 10:41
Оценка: 8 (2) +2
Здравствуйте, Максим, Вы писали:

М>Потом еще один момент. Стандартная библиотека предлагает кучу утилитных методов для метапрограммирования. Часть из них, что-то типа std::is_class можно реализовать, например, используя SFINAE


М>
М>template<typename T>
М>class is_class
М>{
М>    typedef char yes[1];
М>    typedef char no[2];
 
М>    template<typename C>
М>    static yes& test(int C::*); // selected if C is a class type
 
М>    template<typename C>
М>    static no& test(...);       // selected otherwise
М>public:
М>    static bool const value = sizeof(test<T>(0)) == sizeof(yes);
М>};
М>


Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее:

template<typename, typename = void>
struct is_class : std::false_type {};

template<typename T>
struct is_class<T, std::void_t<int T::*>> : std::true_type {};


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

А после перехода на C++20 и эти подходы утратят свою актуальность и будут вытеснены концептами.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 08.02.23 11:02
Оценка:
R>Прежде, чем ответить на основной вопрос, сразу хочу сделать одно замечание, от которого мне просто трудно удержаться. Продемонстрированный подход — это подход двадцатилетней давности, когда в набор инструментов метапрограммирования входили только overload resolution и оператор sizeof. Сейчас этот набор немного побогаче и эта же самая мета-функция может быть реализована значительно элегантнее:

R>
R>template<typename, typename = void>
R>struct is_class : std::false_type {};

R>template<typename T>
R>struct is_class<T, std::void_t<int T::*>> : std::true_type {};
R>



Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?

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


Так ведь других нет (с) Буду очень благодарен за рекомендации хороших книг.

R>А после перехода на C++20 и эти подходы утратят свою актуальность и будут вытеснены концептами.


Я так и думал
Errare humanum est
Re[3]: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 08.02.23 11:09
Оценка: 4 (1)
Здравствуйте, Максим, Вы писали:

М>Спасибо большое! А вот эти std::false_type/std::integral_constant они как-то на уровне компилятора реализуются (в том плане, что программист их написать не может используя просто конструкции языка)?


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

М>Так ведь других нет (с) Буду очень благодарен за рекомендации хороших книг.


Признаться честно, хороших книг в этом плане я даже и не знаю. Могу посоветовать почаще заходить на форум и не стесняться обращаться за подсказками. После приобретения некоторого старового опыта, когда ты прочувствуешь гланый принцип, ты приобретешь и большую самостоятельность в этих вопросах. Увидишь, что польшинство задач можно решить немного по-разному и научишься видедь плюсы и минусы разных решений.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Философско-практические вопросы про метапрограммирование
От: sergii.p  
Дата: 08.02.23 11:46
Оценка: 15 (2) +1
Здравствуйте, Максим, Вы писали:

М>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://www.cppstories.com/2018/05/tmplbook2nd-review/


книга классная. Рекомендую.

М>Другие методы, по моим ощущениям (например std::result_of/std::invoke_result или noexcept operator), можно реализовать только на уровне компилятора.


https://en.cppreference.com/w/cpp/types/result_of
часть Possible implementation

М>Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?


кмк — пустое. Моё мнение, что любую абстракцию надо понимать на частных примерах. Вот шаблоны — частный пример функциональщины. Логично начать с них. Там хотя бы сразу можно увидеть практическое применение.
Re[2]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 08.02.23 12:05
Оценка:
Здравствуйте, sergii.p, Вы писали:

М>>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://www.cppstories.com/2018/05/tmplbook2nd-review/

SP>книга классная. Рекомендую.

С удивлением обнаружил у себя pdf-ку этой книжки. Судя по дате, валяется у меня на диске с сентября 2017-го. Вот думаю, как могло случиться, что она совершенно не отложилась у меня в памяти. Вариант, что я ее не открывал и не просматривал маловероятен.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 08.02.2023 12:06 rg45 . Предыдущая версия .
Re: Философско-практические вопросы про метапрограммирование
От: Voivoid Россия  
Дата: 08.02.23 15:08
Оценка: 8 (2) +1
Здравствуйте, Максим, Вы писали:

М>Как думаете, более глубокое изучение функциональных парадигм и языков (haskell, ocaml или что-то подобное) поможет в этом? Там все таки система типов гораздо серьезней проработана... Или пустое это?


Опыт работы с функциональными языками считаю значительно поможет, т.к.

1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )
2) Шаблонное метапрограммирование иммутабельное, как в чисто функциональных языках

Конечно знакомиться с этими концепциями можно начинать и на C++, но это будет куда как сложее и менее продуктивно, чем если сразу начинать с haskell'а. При этом глубоко изучать haskell ( ну или любой другой чисто функциональный язык ) не надо, достаточно вот этих двух концепций — иммутабельность и паттерн-матчинг. Ну и про унификацию в prolog можно после этого чутка почитать, чтобы понять чем он от pattern матчинга отличается.
Отредактировано 08.02.2023 15:10 Voivoid . Предыдущая версия .
Re[2]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 08.02.23 16:17
Оценка:
V>1) Частичная специализация в C++ это по сути тот же pattern matching в функциональных языках ( точнее частичная специализации это даже круче чем pattern matching, это унификация, как в prolog'е )

Спасибо, только сейчас это осознал!
Errare humanum est
Re: Философско-практические вопросы про метапрограммирование
От: ботаныч Интернет https://youtube.com/shorts/eapWB7W8hEE
Дата: 08.02.23 17:48
Оценка:
Здравствуйте, Максим, Вы писали:

М>Сейчас читаю книгу C++ Templates: The Complete Guide (2nd Edition) by David Vandevoorde, Nicolai M. Josuttis and Douglas Gregor https://www.cppstories.com/2018/05/tmplbook2nd-review/

М>Вот что немного затрудняет продвижение, так это многостраничные объяснения как сделать одно и то же в 11, 14 и 17 плюсах. Отсюда вопрос, на ваш взгляд, имеет ли смысл сейчас запариваться с предыдущими стандартами? Такое ощущение, что через год С++20 уже будет повсеместно, может имеет смысл сразу его осваивать? Там, насколько я понял, куча всяких нововведений типа концептов, которые еще сильнее могут упростить код.
если бы я читал книгу с таким названием, я бы прежде всего посмотрел не на то как в базе (т.е. еще до 11-х плюсов) реализовать is_base_of is_same, или is_virtual и их вариативность, мне было бы интересно как вести себя в современном мире программирования, где подавляющее большинство проектов содержит значительную часть, а зачастую и подавляющую часть легаси. как вести сбя, если есть фрагменты куда и буста то не подложить. Вы эти моменты не вычитывали?
Отредактировано 08.02.2023 17:49 ботаныч . Предыдущая версия .
Re: Философско-практические вопросы про метапрограммирование
От: cppguard  
Дата: 08.02.23 20:08
Оценка: 1 (1) +1 -6 :))) :))
Здравствуйте, Максим, Вы писали:

М>Там все таки система типов гораздо серьезней проработана... Или пустое это?


Нет никакой системы типов. Раньше были программисты, которым вкатывало обмазывать всё макросами. И пофиг на то, что коллеги потом два дня вывод компилятора разбирают, если запятой ошиблись. Потом подвезли шаблоны в С++, и эта кавалерия дружно направилась осванивать их. Возможно, у меня не хватает опыта (хотя работал в фанге на С++ проекте), но я принципиально не понимаю, чем тот же enable_if облегчает задачу. И с ним, и без него мы получаем ошибку компиляции, просто текст разный, но одинаково большой и непонятный. Исключения составляют лишь какая-нибудь хитровытраханная система типов, в которой enable_if волшебно переключает реализации. Я не могу найти пример из головы, но уверен, такое есть. Или какая-нибудь универсальная функция сериализации, которая не должна существовать, если ей передали указатель на ссылку на r-value (добавьте сюда ещё больше сюра). Так что не парься. Если тебе С++ нравится как язык, то практикуйся, смотри видео с CppCon (хотя там редко рассказывают про боевой код, больше про hellow-worldы). А если просто мучает совесть, что не знаешь С++ достаточно хорошо, чтобы за 5 минут погрузить собеседника в сон, то пусть твои сомнения развеют Торвальдс и Беллар. Один написал классную операционку, а второй делает потрясающие проекты в одиночку. И оба не пишут на С++. А из С++-программистов я могу только чемпионов по докладам на конференциях назвать. В корпорациях они, обычно, не пишут боевой код, а входят в "илитную" группу, которая после выхода каждого нового стандарта решает, как переписать всё старое говно на новый лад, или просто тупо форсят идеи корпорации при обсуждении нового стандарта в комитете.

P.S. Мне когда нечего делать, я смотрю доклады по C++ как альтернатива чтению или просмотру научной фантастики.
Re[3]: Философско-практические вопросы про метапрограммирова
От: SaZ  
Дата: 08.02.23 21:01
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>С удивлением обнаружил у себя pdf-ку этой книжки. Судя по дате, валяется у меня на диске с сентября 2017-го. Вот думаю, как могло случиться, что она совершенно не отложилась у меня в памяти. Вариант, что я ее не открывал и не просматривал маловероятен.


А её тут кто-то на форуме выкладывал. Я тоже скачал примерно в то время.
Re[2]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 09.02.23 08:18
Оценка: +2
C> Исключения составляют лишь какая-нибудь хитровытраханная система типов, в которой enable_if волшебно переключает реализации. Я не могу найти пример из головы, но уверен, такое есть.

Посто как пример. Может быть такая задача — вы пишите шаблон класса которому в качестве типа будет передан какой-то контейнер, но вы не знаете какой. Внутри вашего класса вам, по каким-то причинам, надо отсортировать этот контейнер. Причем, если контейнер внутри себя имеет метод sort, то надо дернуть его, а если нет, то вызвать std::sort(cont.begin(), cont.end()). Если нет ни sort, ни begin/end, то должна быть ошибка компиляции. Попробуйте реализуйте такое без SFINAE.
Errare humanum est
Re[3]: Философско-практические вопросы про метапрограммирование
От: rg45 СССР  
Дата: 09.02.23 12:01
Оценка:
Здравствуйте, Максим, Вы писали:

М>Посто как пример. Может быть такая задача — вы пишите шаблон класса которому в качестве типа будет передан какой-то контейнер, но вы не знаете какой. Внутри вашего класса вам, по каким-то причинам, надо отсортировать этот контейнер. Причем, если контейнер внутри себя имеет метод sort, то надо дернуть его, а если нет, то вызвать std::sort(cont.begin(), cont.end()). Если нет ни sort, ни begin/end, то должна быть ошибка компиляции. Попробуйте реализуйте такое без SFINAE.


А если контейнер уже отсортирован (map, set...), то ничего не делать. В этом случае процедура сортировки существует лишь номинально.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Философско-практические вопросы про метапрограммирование
От: Максим Россия  
Дата: 09.02.23 12:48
Оценка:
R>А если контейнер уже отсортирован (map, set...), то ничего не делать. В этом случае процедура сортировки существует лишь номинально.

Заинтриговали А по какимм критериям можно определить, что контейнер не нуждается в дополнительной сортировке?
Errare humanum est
Re[5]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 13:05
Оценка: 6 (2)
Здравствуйте, Максим, Вы писали:

М>Заинтриговали А по какимм критериям можно определить, что контейнер не нуждается в дополнительной сортировке?


Ну стандартные контейнеры std::map, std::set (multimap, multiset) по природе своей всегда отсортированы:

http://coliru.stacked-crooked.com/a/3ff8eb5d29c281a4

#include <iostream>
#include <set>

int main()
{
    std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
    
    for (const int value : set)
        std::cout << " " << value; // -> 0 1 2 3 4 5 6 7 8 9
}


А, по каким критериям? Ну, проще всего это сделать через traits-ы. Ты свою обобщенную процедуру сразу делаешь заточенной на использование трейтсов. А в трейтсах уже можно навернуть аналитику любой сложности — от автоматического определения до ручной специализации. Использование трейтсов делает обощенные процедуры легко расширяемыми. Этот подход можно применить не ко всей обобщенной процедуре, а только к внутренней процедуре сортировки. В общем, все в зависимости от задачи.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 09.02.2023 13:15 rg45 . Предыдущая версия . Еще …
Отредактировано 09.02.2023 13:12 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 13:09 rg45 . Предыдущая версия .
Re[6]: Философско-практические вопросы про метапрограммирова
От: Максим Россия  
Дата: 09.02.23 13:19
Оценка: +1
R>А, по каким критериям? Ну, проще всего это сделать через traits-ы. Ты свою обобщенную процедуру сразу делаешь заточенной на использование трейтсов. А в трейтсах уже можно навернуть аналитику любой сложности — от автоматического определения до ручной специализации. Использование трейтсов делает обощенные процедуры легко расширяемыми. Этот подход можно применить не ко всей обобщенной процедуре, а только к внутренней процедуре сортировке. В общем, все в зависимости от задачи.

А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!

UPD
Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>
Errare humanum est
Отредактировано 09.02.2023 14:33 Максим . Предыдущая версия . Еще …
Отредактировано 09.02.2023 13:27 Максим . Предыдущая версия .
Re[7]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 13:44
Оценка: 4 (1)
Здравствуйте, Максим, Вы писали:

М>А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!


Да, чуть позже только, сейчас немного дефицит со временем.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 14:51
Оценка:
Здравствуйте, Максим, Вы писали:

М>UPD

М>Еще, наверно, можно попробовать оттолкнуться от std::is_same_v<T, std::set>

Можно и так, а можно и по-другому, я потом предложу свой вариант.

P.S. Спасибо тебе за оценки, которые ты мне ставишь, но смотри не перестарайся, тут за большое количество оценок могут и оштрафовать: http://rsdn.org/Forum/Info/info.forum.rating.aspx
Автор: IT
Дата: 16.04.03
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: Философско-практические вопросы про метапрограммирова
От: Максим Россия  
Дата: 09.02.23 15:25
Оценка:
R>P.S. Спасибо тебе за оценки, которые ты мне ставишь, но смотри не перестарайся, тут за большое количество оценок могут и оштрафовать: http://rsdn.org/Forum/Info/info.forum.rating.aspx
Автор: IT
Дата: 16.04.03


Я и не думал, что тут такая развитая система рейтингов
Errare humanum est
Re[7]: Философско-практические вопросы про метапрограммирова
От: rg45 СССР  
Дата: 09.02.23 16:00
Оценка: 32 (4)
Здравствуйте, Максим, Вы писали:

М>А можете, если есть время, более подробно написать (может пару строчек кода) как такое делается? У меня пока только совсем простые вещи получаются, типа проверок есть ли определенный метод в классе или нет. А те же begin/end в set есть и просто так не отделить set от vector. Что-то типа std::is_set_v<T> вроде нет в библиотеке. Вероятнее всего, я пока просто не понимаю, что такое traits в плюсах . Спасибо за помощь!


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

Хочется сразу сделать небольшое замечание по поводу traits-ов. В традиционном понимании traits-ы — это шаблонный клас со специализациями, содержащий в себе фиксированный набор свойств типов, которые используются в определенной обобщенной процедуре (или группе процедур). В этом примере использован немного другой подход. Traits-ы здесь представлены набором объявлений функций — именно объявлений, без определений. Эти объявления логически играют ту же самую роль — связывают между собой различные сущности времени компиляции — типы и константы. Но, по сравнению с классическим подходом, подход с фунциями имеет некоторые существенные преимущества: они допускают различную группировку по типам для разных свойств, а еще функции подчиняются правилам overload resolution и поиска по ADL. Трейтсы-функции объявляются в тех же пространствах имен, что и сами типы, прямо рядышком. Это здорово облегчает определение трейтсов и способствует комфортной расширяемости обобщенных процедур для пользовательских типов. Но есть и минус: трейтсы для стандартных типов и типов из сторонних библиотек должны быть видимы в точке использования (в метафункциях) и их приходится определять заранее. Но как по мне, это не большая плата за те преимущества, которые дает этот подход, а где-то это даже и логично.

http://coliru.stacked-crooked.com/a/f9923f0f88685d13

#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <utility>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Generic Library Area

template <typename, typename = void>
struct has_sort_member_function : std::false_type {};

template <typename T>
struct has_sort_member_function<T, std::void_t<decltype(std::declval<T&>().sort())>> : std::true_type {};

template <typename, typename = void>
struct is_std_sort_applicable : std::false_type{};

template <typename T>
struct is_std_sort_applicable<T, std::void_t<
   decltype(std::sort(std::begin(std::declval<T&>()), std::end(std::declval<T&>()))),
   decltype(*std::begin(std::declval<T&>()) = *std::begin(std::declval<T&>()))
>> : std::true_type {};

template <typename T>
auto assume_sorted_range(const T&) -> std::false_type;

// For standard containers, the traits must be defined beforehand
template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::map<K, T, C, A>&) -> std::true_type;

template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::multimap<K, T, C, A>&) -> std::true_type;

template <typename T, typename C, typename A>
auto assume_sorted_range(const std::set<T, C, A>&) -> std::true_type;

template <typename T, typename C, typename A>
auto assume_sorted_range(const std::multiset<T, C, A>&) -> std::true_type;

template <typename T>
using is_sorted_container = decltype(assume_sorted_range(std::declval<T>()));

template <typename T, std::enable_if_t<has_sort_member_function<T>::value, int> = 0>
void sort(T& t)
{
   t.sort();
}

template <typename T, std::enable_if_t<
   is_std_sort_applicable<T>::value &&
   !has_sort_member_function<T>::value &&
   !is_sorted_container<T>::value,
int> = 0>
void sort(T& t)
{
   std::sort(std::begin(t), std::end(t));
}

template <typename T, std::enable_if_t<
   is_sorted_container<T>::value &&
   !has_sort_member_function<T>::value,
int> = 0>
void sort(const T&)
{
   // Container is sorted already, so nothing to do.
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Application Area

struct MyContainer
{
   void sort() { std::cout << "MyContainer::sort" << std::endl; }
};

struct MySortedContainer
{

};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;

int main()
{
   std::string string = "Hello World!";
   sort(string); // call to std::sort
   std::cout << "<" << string << ">" << std::endl;

   int array[] { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   sort(array); // call to std::sort

   std::vector<int> vector { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   sort(vector); // call to std::sort

   std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   sort(set); // no effect

   MyContainer my_container;
   sort(my_container); // call to MyContainer::sort

   MySortedContainer my_sorted_container;
   sort(my_sorted_container); // no effect

   char str[] = "Hello World!";
   sort(str); // call to std::sort
   // sort("Hello World!"); // error: no matching function for call
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 09.02.2023 20:11 rg45 . Предыдущая версия . Еще …
Отредактировано 09.02.2023 20:09 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 20:00 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 16:22 rg45 . Предыдущая версия .
Отредактировано 09.02.2023 16:06 rg45 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.