Pull to refresh

Comments 77

Чёрт, тип string до сих пор не встроен в ядро языка, а реализован как шаблон в Standard Library.
Если автору нужно, чтобы было как в Python, пусть программирует на Python. Не надо превращать C++ в еще один C#.
Все это нытье про необходимоть добавить в C++ рефлекшн и сборку мусора вызывает беспокойство. На это почему-то не обращают внимание, но в C++11 стандартная библиотека теперь местами интегрируется с языком. Например, лямбда в общем случае может иметь тип только std::function, что лишает разработчика возможности создать альтернативу, а язык его гибкости и прозрачности, одного из мощных достоинств.
Статья не понравилась, какой-то плохо структурированный поток мыслей. Или перевод такой.
А по-моему, как раз насчет строк автор прав. Мощность языка это, конечно, клёво, но когда каждая 2я библиотека имеет свою реализацию строк и вообще всего на свете — это печально. И уж точно не увеличивает совместимость библиотек между собой, простоту и доступность языка для новых разработчиков.

Конечно, некоторые фичи в плюсах смотрелись бы глупо, но отрицать очевидные недостатки языка тоже не стоит.
Я не совсем понимаю, в чём конкретно он прав? Класс std::string является стандартным, и в этом смысле он ничуть не менее стандартен, чем строки в Питоне или C#. Кто-то реализует альтернативы, но обычно у этих реализаций уши растут из девяностых, когда стандарта действительно не было, но с этим ничего не сделаешь. Если завтра в C++ введут ключевое слово string без префикса std::, ничего не изменится, и альтернативные реализации никуда не денутся.

А про всё на свете — это, к сожалению, плата за стандарт ISO, который принимается медленно. Скажем, стандартная многопоточность только сейчас появилась, хотя Страуструп давно об этом говорил. Если бы у него была власть ван Россума, эту проблему решили бы десять лет тому назад.
Я думаю, тут проблема в бинарной совместимости.
Если язык managed или использует VM, string из разных модулей runtime-среда приведёт к одному типу.
Но для C/C++ на уровне ABI стандартизирован только обмен классами с описанными virtual функциями, без доступа к полям (например, как в COM), а это громоздко.

Я видел небрежно спроектированную программу, которая подключает плагины с использованием string в интерфейсе (скомпиленную с STLPort 4). В какой-то версии произошло обновление до STLPort 5, все плагины слетели.
Понимаю. Наверно, это потенциальная проблема любого компилируемого в объектный код языка.
когда каждая 2я библиотека имеет свою реализацию строк и вообще всего на свете — это печально
Кому нужно решение по умолчанию, используют std::string. Лишать остальных права выбора и контроля над своей программой на базовом уровне не вижу оснований. В этом весь дух C++, убрать его, значит убить язык. Наверное, некоторые именно этого и добиваются.

простоту и доступность языка для новых разработчиков
Мне неочевидна истинность утверждения, что для новых разработчиков C++ должен быть во всем простым и доступным.
У std::string всё-таки есть сложности с юникодом, в таких случаях проще использовать QString, которая просто нормально работает, чем изучать особенности кодирования юникода в конкретной платформе.
Ну, сейчас уже есть стандартные библиотечные средства перевода, так что эта проблема тоже по идее решена.
Всё-таки в Qt они удобнее :) Да и QtCore собирается нынче для всего на свете и он не такой толстый, как boost, хотя жаль, что они свою библиотеку контейнеров не сделали отдельным модулем, чтобы тащить только её, но не тащить всякие QObject штуки.
Ну с этим я не спорю :) Но если в эту сторону рассуждать, про любой язык программирования можно сказать, что то или иное стандартное средство в нём менее удобно сделано, чем сторонняя библиотека.
Включение строк в язык сильно противоречит идеологии нулевой стоимости, которая гласит, что не используемая вами фича должна вам обходиться бесплатно. Лямбда разве не анонимный функтор?
Cогласен, С++ дает возможность делать то, что именно нужно, а все дополнительные фишки должны быть дополнительными.
C++ дает слишком много возможностей :)
Вы так говорите, будто это что-то плохое.
лямбда в общем случае может иметь тип только std::function

Что? Вы явно что-то неправильно поняли про лямбды. Никто не мешает завернуть лямбду в std::function, но сама лямбда имеет другой тип — «unique unnamed nonunion class type», как сказано в стандарте.
Ниже уже написал, что ошибался. Отредактировать исходное сообщение не могу, к сожалению. Прошу прощения.
Просто когда некоторое время назад изучал этот вопрос, или действительно что-то неправильно понял, или используемый на тот момент компилятор был недостаточно функционален, но почему-то запомнился именно такой вывод. Сейчас проверил, действительно все работает, как надо.
Остается еще std::initializer_list, как тут заметили. Ну и вообще я про намечающуюся тенденцию, которая лично меня настораживает.
Ну да. Еще пример можно привести из той же оперы: оператор typeid возвращает ссылку на std::type_info.
А чем вызвана ваша обеспокоенность в случае с std::initializer_list и std::type_info?

Использование std::initializer_list не обязательно, более того, чтобы его использовать, нужно явно сказать компилятору. Он не имеет overhead'а. Написать библиотечный класс с аналогичной функциональностью без вмешательства в работу компилятора невозможно. В этом принципиальное отличие std::initializer_list от std::vector/std::string, которые можно реализовать без изменения компилятора.
std::initializer_list без проблем реализуется без изменения компилятора. пруф чуть ниже.
Всё-таки отличия и проблемы есть:
1. Копирование std::initializer_list не приводит к копированию элементов
2. N виртуальных вызовов на инициализацию массива из N элементов это слишком. Лучше уж взять boost::assign, хотя у него синтаксис похуже
3. std::initializer_list поддерживает begin/end, что позволяет использовать его в range-base-for
Например, лямбда в общем случае может иметь тип только std::function, что лишает разработчика возможности создать альтернативу

на самом деле лямбда это просто класc с переопределённым оператором (), так что если реализовывать свой делегат, то проблем это не вызовет.
на самом деле лямбда это просто класc с переопределённым оператором ()
Подразумевалась альтернатива std::function, как элементу пусть и стандартной, но библиотеки, а не лямбде, которая как раз удобный синтаксический сахар языка.
Подразумевалась альтернатива std::function

ну так я про неё и говорю, написать свою альтернативу, которая принимает лямбду не проблема
Вы о чем?
Пока не вижу примера, как поймать лямбду без использования std::function, зато вижу минус на просьбу продемонстрировать, что "написать свою альтернативу, которая принимает лямбду не проблема".
Благодарю, был неправ.
auto hello = [] () {  std::cout << "Hello, world!" << std::endl; };
auto hello_100 = [hello] () { for (int i = 0; i < 100; ++i) hello(); };
Это не то, пример ничего не говорит про суть типа hello, неявно он вполне мог бы быть и std::function.
Вот здесь достаточный пример уже был приведен.
Не может он быть std::function. Между
auto hello = [] () { std::cout << «Hello, world!» << std::endl; };
и
std::function<void(void)> hello = [] () { std::cout << «Hello, world!» << std::endl; };
есть большая разница.
Std::function использует malloc и указатели для полиморфизма в рантайме, а лямбда — это просто анонимная структура с перегруженным (), ваш пример с auto никуда в кучу не лезет.
Std::function использует malloc и указатели для полиморфизма в рантайме, а лямбда — это просто анонимная структура с перегруженным (), ваш пример с auto никуда в кучу не лезет.
Все это следует из текста примера?
Напомню, что речь была про демонстрацию возможности захватить лямбду собственной структурой данных.
А вот насчёт этого, без понятия, ещё не ковырял, но наверняка это всё также оборачивается в какой-то неопределённый класс + шаблонная магия ))
Намного посидел вечером и решил разобраться со списками инициализации, в общем std ненужен, вот пруф
liveworkspace
Элементы в памяти расположены в обратном порядке, как параметры в стеке.

Я просто привёл простейшее решение, можно всё сделать совершенно по другому, просто хотел показать, что чтобы пройтись по элементам std::initializer_list ненужен, и можно написать свой аналог, также как и в случае с std::function.
+ ещё список проблем:

То что я привёл, ни в коем случае не претендует на эталонную реализацию, или на реализацию которая имела бы полный список возможностей std::initializer_list. Как я уже сказал просто хотелось продемонстрировать что и без std::initializer_list можно использовать списки.
Я просто привёл простейшее решение, можно всё сделать совершенно по другому, просто хотел показать, что чтобы пройтись по элементам std::initializer_list ненужен, и можно написать свой аналог
Чтобы данный (модифицированный) пример корректно работал, нужно скопировать каждый элемент внутрь списка, т.е. это перебор отдельных копий, а не элементов исходного списка, представленных в виде непрерывного массива. Строго говоря, принципа заменяемости initializer_list пример пока не демонстрирует.
Ок вот другой вариант, ещё лучше и проще.
Принципиальной разницы не появилось, параметры все так же на стеке в обратном порядке, о доступе к массиву элементов речи нет.

Тут копируются только указатели.
Указатели на параметры в стеке, которые недействительны после выхода из конструктора списка?

(я таких могу ещё кучу придумать)
Зачем куча, если нужен один, воспроизводящий поведение initializer_list.
Принципиальной разницы не появилось, параметры все так же на стеке в обратном порядке, о доступе к массиву элементов речи нет.

Так я и не делаю аналог initializer_list, я хочу показать что и без него можно пройтись по списку и получить то что нужно.
Указатели на параметры в стеке, которые недействительны после выхода из конструктора списка?
угу но, зато можно по нему потом пройтись как по массиву без проблем и скопировать данные куда угодно.
Зачем куча, если нужен один, воспроизводящий поведение initializer_list.

ещё бы знать как он внутри устроен, я же на угад всё делал.
UFO just landed and posted this here
с меньшей вероятностью сделают всё ту же старую ошибку, написав что-то вроде «for (i=0; i<=length;i++)», а потом ещё будут спрашивать вас, почему этот код иногда работает, а иногда падает внутри функций, из которых был вызван

Имеется ввиду запись во вне аллоцированной памяти? или есть еще какая-то причина поведению: «почему этот код иногда работает, а иногда падает внутри функций»?
Лучше объясните, почему в C++ до сих пор нет выборочной инициализации структур и массивов как в C99?

struct structure {
int a;
int b;
};

enum {
ITEM_A,
ITEM_B,
NUM_ITEMS
};

static const struct structure item_a = {
.a = 1,
};

static const struct structure item_b = {
.b = 2,
};

static const struct structure* const array[NUM_ITEMS] = {
[ITEM_A] = item_a,
[ITEM_B] = item_b,
};
Потому что такое поведение без последствий можно реализовать только для POD-типов. Но тогда уж лучше этого не вводить и оставить язык консистентным. Конструкторы и списки инициализации спасают во многих случаях.
Я не понимаю зачем сравнивать языки с управлением памятью и C++. Это как сравнивать лопату и утюг. И то и то инструмент, но для разных вещей.

C++11 привнес в язык очень много чего полезного и нужного. Сейчас даже не представлю как писать на C++03. Очень жаль, что так поздно, язык потерял много сторонников.

На счёт libstdc++, там из действительно нужного остаются нереализованными регэкспы и emplace для некоторых контейнеров.
Да, но все равно мне libc++ больше нравится, а она в общем-то и с gcc вроде бы работает :)
А с portability что? Сильно приходится менять при компиляции gcc. Я вот всё думаю на clang переползти, но пока по скорости он проигрывает.
Я ни строчки не поменял :) Но я не слишком активно юзаю stl, boost же собирается с это библиотекой.
clang я использую для debug сборок, скорость не так критична, а вот диагностика уже на первый план выходит.
Это хорошо, у меня какие-то ворнинги выдавал вроде.

Ну в бусте полно #ifdef #else чем забитвать свой код не охота совершенно.
UFO just landed and posted this here
При таком числе тянущих на себя одеяло это сложно.
Я люблю C++, а C++11 еще больше, и использование Boost-а тоже приветствую, но статья, извините, просто ни о чем.
Пустой треп про стандарты и комитеты. Не увидел никакого сравнения.
Статья сравнивает тёплое с мягким. Язык-языком, стандартная библиотека — ок, буст — тоже ок. С чего бы их сравнивать? Выглядит как попытка защитить буст. Как-будто его зачем-то надо защищать. В общем, странный текст.
Хотите list в Python? Просто наберите «a = [1,2,3]». Готово.

Как это мило — сравнивать интерпретируемый язык с динамической типизацией с компилируемым языком со строгой типизацией.

std::vector<int> a = {1,2,3}; //Готово.

В C++ слабая типизация (нестрогая):
C++ — Статическая | Слабая | Явная


Наверное, подразумевалась статическая или явная типизация.
Да, подразумевалась явная типизация. Спасибо за ликбез.
Это же вроде как совсем недавно появилось?

при этом в языке со строгой типизацией можно прострелить себе не только ногу, но и голову

UFO just landed and posted this here
Меня QT немного пугает необходимостью использования дополнительного препроцессора.
Если вы не используете специфических вещей, типа сигналов и слотов, использование препроцессора необязательно.
Это не препроцессор, а кодогенератор, исходники там стопроцентный С++. И вообще, у них был в планах проект по написанию плагина для clang'а вместо юзания moc'а, но пока дальше слов дело не пошло.
UFO just landed and posted this here
Это вы просто не сталкивались, наверное, с побочными эффектами COW типа непотокобезопасности контейнеров при чтении и обязталеьной возможностью скомпилировать detach, с недоделанными умными указателями и с другими интересными вещами.
По-моему, a priori предполагать потокобезопасность контейнеров вообще никогда не стоит
На чтение контейеры обычно потокобезопасны. Вот, например, что говорит MSDN.

Qt тоже утверждает, что контейнеры потокобезопасны на чтение.

Вот только проблема в том, что взятие итератора от контейнера — это уже запись с точки зрения Qt. При взятии итератора будет вызван detach, который не потокобезопасен:

QVector<int> v;
QVector<int>::const_iterator it = v.begin(); // does no thread safe
QVector<int>::const_iterator it2 = v.constBegin(); // DOES thread safe

Вторая — джуниоры(или даже сеньоры, которые, как вы думаете, никогда не должны быть повышены) с меньшей вероятностью сделают всё ту же старую ошибку, написав что-то вроде «for (i=0; i<=length;i++)», а потом ещё будут спрашивать вас, почему этот код иногда работает, а иногда падает внутри функций, из которых был вызван.


Объясните, пожалуйста, что имелось ввиду здесь под старой ошибкой?
В чём она?

UPD:
Всё понял…
i должно быть строго меньше length.
Прошу прощения
Может я гоню жестоко, но почему это
i должно быть строго меньше length.
О____О
Потому что в массиве длиной 10 последний элемент будет под номером 9.(Нумерация начинается с 0)
Про массив в тексте вообще ни слова не было. Этот «сферический цикл в вакууме», не предполагающий что итерация происходит по массиву.
В статье речь шла про конструкцию foreach, которая используется для итерации по контейнерам. И приведен пример, что мы могли бы использовать нативный вариант с циклом. Поэтому для контейнеров, которые предполагают индексацию, такая итерация, скорее всего будет ошибочной.
В «сферическом цикле в вакууме» искать ошибки вообще нет смысла. Поэтому необходим контекст, в котором такая конструкция может привести к ошибке.
Я для себя его определил как итерация по коллекции.
Логично! Теперь вижу, что, действительно, подразумевался контейнер!
Sign up to leave a comment.

Articles