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++ должен быть во всем простым и доступным.
UFO landed and left these words here
Ну, сейчас уже есть стандартные библиотечные средства перевода, так что эта проблема тоже по идее решена.
UFO landed and left these words here
Ну с этим я не спорю :) Но если в эту сторону рассуждать, про любой язык программирования можно сказать, что то или иное стандартное средство в нём менее удобно сделано, чем сторонняя библиотека.
UFO landed and left these words here
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

ну так я про неё и говорю, написать свою альтернативу, которая принимает лямбду не проблема
UFO landed and left these words here
Вы о чем?
Пока не вижу примера, как поймать лямбду без использования std::function, зато вижу минус на просьбу продемонстрировать, что "написать свою альтернативу, которая принимает лямбду не проблема".
UFO landed and left these words here
Благодарю, был неправ.
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 landed and left these words 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 для некоторых контейнеров.
UFO landed and left these words here
А с portability что? Сильно приходится менять при компиляции gcc. Я вот всё думаю на clang переползти, но пока по скорости он проигрывает.
UFO landed and left these words here
Это хорошо, у меня какие-то ворнинги выдавал вроде.

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

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

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

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


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

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

UFO landed and left these words here
Меня QT немного пугает необходимостью использования дополнительного препроцессора.
Если вы не используете специфических вещей, типа сигналов и слотов, использование препроцессора необязательно.
UFO landed and left these words here
UFO landed and left these words 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