Комментарии 28
Часто из-за глубокой вложенности функциональный синтаксис композиции представлений в плане написания и чтения может быть очень громоздким.
Не означает ли это, что давно пора переработать синтаксис самого C++, чтобы избежать излишней громоздкости?
С++2.0 в массы!
Интересно было бы посмотреть на язык программирования ++С.
Если я ещё/уже не забыл C/C++ (давно не использовал), префиксная запись означает, что, сначала, производится инкремент (то есть — приращение), а уже затем возвращается результат. В постфиксной, вроде бы, всё наоборот, сначала возвращается исходное значение, но, затем, происходит инкремент. То есть: результат операции (возвращаемое значение) является различным, но побочный эффект (в виде инкремента заданной переменной) остаётся одним и тем же. Я ничего не путаю?
Раньше имелось в виду, что C++ — это тот же язык C (синтаксис — результат), но с новым смыслом (семантика — побочный эффект). Вот я и думаю, что мог бы из себя представлять язык программирования ++С, у которого уже сразу изменён синтаксис, и этот новый синтаксис обеспечивает и новую семантику. В C++ изменение семантики происходило за счёт того, что каждую переменную можно было мыслить как экземпляр некоторого класса. Отсюда и расхожее представление, что C++ — это "C с классами".
Я мог бы предположить, только, что язык программирования ++С — это язык программирования, в котором сделан упор на перегрузку операторов. Должен действовать принцип: "всё есть оператор и всё может быть перегружено". Это значит, что и обыкновенные скобки должны стать полноценным оператором. Это придаст языку программирования С некоторые черты Python'а. Однако, такой подход означает, что и оператор for тоже может быть перегружен.
Верните нам наш Турбо Си 2.01 и МС Дос 6.0 !
(Нет, лучше закопайте их поглубже...)
Не означает ли это, что давно пора переработать синтаксис самого C++, чтобы избежать излишней громоздкости?
А как же обратная совместимость?
В C# есть элегантное решение: методы-расширения, когда первый аргумент становится, типа, объектом, а статическая функция — типа, методом. Вместо
ClassName.Reverse(ClassName.Take(ClassName.Reverse(x), 3));
можно писать
x.Reverse().Take(3).Reverse();
В C++ же такое невозможно, т.к. сломает ADL.
Поясните, пожалуйста, подробнее. Про ADL. Сам язык программирования, вроде бы, позволяет это сделать:
class Foo
{
Foo& SomeMethod(){ return *this; };
}
Это и ломает ADL?
Нет. Я про Uniform Function Call Syntax.
Я большой любитель ADL и не в курсе, как UFCS его ломает. Может быть есть примеры? Если взять идею из D, где не надо ничего указывать специально и любая функция может быть вызвана как метод на своём первом аргументе, то алгоритм простой:
1) Ищем метод на объекте, если есть зовём
2) Если нет, то ищем функцию по старым правилам, включая ADL.
Что не так?
Я большой любитель ADL и не в курсе, как UFCS его ломает.
Не помню, давно про это читал, в голове почему-то отложилось.
Что не так?
Всё логично. Но proposal-ы стабильно не проходят голосование, потому что вечно находятся причины против UFCS: каша с overload resolution или же необходимость всюду писать using namespace и т.д.
Но proposal-ы стабильно не проходят голосование, потому что вечно находятся причины против UFCS
Вот это, к сожалению, правда. Даже версия с вывернутым наизнанку UFCS не прошла, когда предлагали begin(vec) трактовать как vec.begin(), а не наоборот. В том же D на этом Ranges работают и не надо дополнительно перегружать |. Я не всегда понимаю комитет, порой они уж слишком осторожны.
и не надо дополнительно перегружать |
Идея с перегрузкой оператора как раз выглядит логичной, потому что не создаёт проблем, пусть и приводит к несколько уродливому синтаксису.
Больная проблема C++ — это пространства имён. Если вы хотим вызывать метод-расширение, то мы должны как-то сообщить компилятору, где его искать. В общем случае метод-расширение находится в отдельном пространстве имён, и для обычного вызова функции мы бы написали:
ns::ext::take(list, 3)
В случае расширения нам пришлось бы писать:
list.ns::ext::take(3)
Ужасно, не правда ли? Конечно, мы можем обмазаться using
-ами, но это будет так себе решение.
А дальше мы вспоминаем, что в C++ есть ADL, и пытаемся заставить его работать: а именно, сделать так, чтобы один из аргументов функции был из нужного нам пространства имён. Хорошим кандидатом на эту роль оказались бинарные операторы:
list | ns::ext::take(3)
P.S. Собственно, вот что я имел виду, когда писал, что расширения ломают ADL — он попросту не работает с ними.
И в C++ так можно https://github.com/DevUtilsNet/linqcpp
В той библиотеке все методы LINQ определены в классе Shim<T>
из файла linqcpp.h
Где расширения-то?
Там используется костыль в виде From(...)
.
Ну и дичь вроде такой:
auto from = From( vector );
auto where = from.Select< std::unique_ptr< int >& >( []( std::unique_ptr< int >& m ) { return std::ref( m ); } );
…
auto v_select =
From( { std::vector< TestType2 >( { TestType2( 1 ) } ), std::vector< TestType2 >( { TestType2( 1 ) } ) } )
.SelectMany< TestType2 >( []( const std::vector< TestType2 >& s ) {
return From( std::vector< TestType2 >( s ) ).Concat( std::vector< TestType2 >( { TestType2( 1 ) } ) );
} )
.Select< int >( []( const TestType2 t ) { return t._t; } )
.ToVector();
Языку C++ очень не хватает компактного синтаксиса для определения лямбд.
C++ обладает слишком огромным наследием, чтобы просто переработать даже малые участки языка. Из-за этого приходится тащить все старое, даже если этим больше никто не пользуется, иначе мы сломаем старый код и испортим совместимость.
Вместо переработки языка в целом, появляются иные языки, что так же высоки по уровню и с возможностью быть "ближе к железу" - Rust к примеру
вот это поворот. А что говорят отцы например Шон Парент?
то ли у меня проблемы с восприятием, то ли перевод машинный и "кривой" по части терминологии
C++20 Ranges — Полное руководство