Во время поездки мы обедали с Бьярне Строуструпом, катались в лифте с Гербом Саттером, жали руку Беману Дейвсу, выходили «подышать воздухом» с Винцентом Боте, обсуждали онлайн-игры с Гором Нишановым, были на приёме в мэрии Оулу и общались с мэром. А ещё мы вместе со всеми с 8:30 до 17:30 работали над новым стандартом C++, зачастую собираясь в 20:00, чтобы ещё четыре часика поработать и успеть добавить пару хороших вещей.
Теперь мы готовы поделиться с вами «вкусностями» нового стандарта. Всех желающих поглядеть на многопоточные алгоритмы, новые контейнеры, необычные возможности старых контейнеров, «синтаксический сахар» нового чудесного C++, прошу под кат.
if constexpr (condition)
В C++17 появилась возможность на этапе компиляции выполнять if:
template <std::size_t I, class F, class S>
auto rget(const std::pair<F, S>& p) {
if constexpr (I == 0) {
return p.second;
} else {
return p.first;
}
}
При этом неактиваная ветка ветвления не влияет на определение возвращаемого значения. Другими словами, данный пример скомпилируется и:
- при вызове rget<0>( std::pair<char*, short>{} ) тип возвращаемого значения будет short;
- при вызове rget<1>( std::pair<char*, short>{} ) тип возвращаемого значения будет char*.
T& container::emplace_back(Args&&...)
Методы emplace_back(Args&&...) для sequence контейнеров теперь возвращают ссылку на созданый элемент:
// C++11
some_vector.emplace_back();
some_vector.back().do_something();
// C++17
some_vector.emplace_back().do_something();
std::variant<T...>
Позвольте представить: std::variant<T...> — union, который помнит что хранит.
std::variant<int, std::string> v;
v = "Hello word";
assert(std::get<std::string>(v) == "Hello word");
v = 17 * 42;
assert(std::get<0>(v) == 17 * 42);
Дизайн основан на boost::variant, но при этом убраны все известные недочёты последнего:
- std::variant никогда не аллоцирует память для собственных нужд;
- множество методов std::variant являются constexpr, так что его можно использовать в constexpr выражениях;
- std::variant умеет делать emplace;
- к хранимому значению можно обращаться по индексу или по типу — кому как больше нравится;
- std::variant не нуждается в boost::static_visitor;
- std::variant не умеет рекурсивно держать в себе себя (например функционал наподобие `boost::make_recursive_variant<int, std::vector< boost::recursive_variant_ >>::type` убран).
Многопоточные алгоритмы
Практически все алгоритмы из заголовочного файла были продублированы в виде версий, принимающих ExecutionPolicy. Теперь, например, можно выполнять алгоритмы многопоточно:
std::vector<int> v;
v.reserve(100500 * 1024);
some_function_that_fills_vector(v);
// Многопоточная сортировка данных
std::sort(std::execution::par, v.begin(), v.end());
Осторожно: если внутри алгоритма, принимающего ExecutionPolicy, вы кидаете исключение и не ловите его, то программа завершится с вызовом std::terminate():
std::sort(std::execution::par, v.begin(), v.end(), [](auto left, auto right) {
if (left==right)
throw std::logic_error("Equal values are not expected"); // вызовет std::terminate()
return left < right;
});
Доступ к нодам контейнера
Давайте напишем многопоточную очередь с приоритетом. Класс очереди должен уметь потокобезопасно сохранять в себе множество значений с помощью метода push и потокобезопасно выдавать значения в определенном порядке с помощью метода pop():
|
|
В C++17 многие контейнеры обзавелись возможностью передавать свои внутренние структуры для хранения данных наружу, обмениваться ими друг с другом без дополнительных копирований и аллокаций. Именно это происходит в методе pop() в примере:
// Извлекаем из rbtree контейнера его 'ноду' (tree-node)
auto node = values_.extract(values_.begin());
// Теперь values_ не содрежит в себе первого элемента, этот элемент полностью переехал в node
// values_mutex_ синхронизирует доступ к values_. раз мы вынули из этого контейнера
// интересующую нас ноду, для дальнейшей работы с нодой нет необходимости держать блокировку.
lock.unlock();
// Наружу нам необходимо вернуть только элемент, а не всю ноду. Делаем std::move элемента из ноды.
return std::move(node.value());
// здесь вызовется деструктор для ноды
Таким образом наша многопоточная очередь в C++17 стала:
- более производительной — за счёт уменьшения количества динамических аллокаций и уменьшения времени, которое программа проводит в критической секции;
- более безопасной — за счёт уменьшения количества мест, кидающих исключения, и за счет меньшего количества аллокаций;
- менее требовательной к памяти.
Автоматическое определение шаблонных параметров для классов
В черновик C++17 добавили автоматическое определение шаблонных параметров для шаблонных классов. Это значит, что простые шаблонные классы, конструктор которых явно использует шаблонный параметр, теперь автоматически определяют свой тип:
Было | Стало |
---|---|
|
|
|
|
|
|
std::string_view
Продолжаем эксурс в дивный мир C++17. Давайте посмотрим на следующую C++11 функцию, печатающую сообщение на экран:
// C++11
#include <string>
void get_vendor_from_id(const std::string& id) { // аллоцирует память, если большой массив символов передан на вход вместо std::string
std::cout <<
id.substr(0, id.find_last_of(':')); // аллоцирует память при создании больших подстрок
}
// TODO: дописать get_vendor_from_id(const char* id) чтобы избавиться от динамической аллокации памяти
В C++17 можно написать лучше:
// C++17
#include <string_view>
void get_vendor_from_id(std::string_view id) { // не аллоцирует память, работает с `const char*`, `char*`, `const std::string&` и т.д.
std::cout <<
id.substr(0, id.find_last_of(':')); // не аллоцирует память для подстрок
}
std::basic_string_view или std::string_view — это класс, не владеющий строкой, но хранящий указатель на начало строки и её размер. Класс пришел в стандарт из Boost, где он назывался boost::basic_string_ref или boost::string_ref.
std::string_view уже давно обосновался в черновиках C++17, однако именно на последнем собрании было решено исправить его взимодействие с std::string. Теперь файл <string_view> может не подключать файл <string>, за счет чего использование std::string_view становится более легковесным и компиляция программы происходит немного быстрее.
Рекомендации:
- используйте единственную функцию, принимающую string_view, вместо перегруженных функций, принимающих const std::string&, const char* и т.д.;
- передавайте string_view по копии (нет необходимости писать `const string_view& id`, достаточно просто `string_view id`).
Осторожно: string_view не гарантирует, что строчка, которая в нем хранится, оканчивается на символ '\0', так что не стоит использовать функции наподобие string_view::data() в местах, где необходимо передавать нуль-терминированные строчки.
if (init; condition)
Давайте рассмотрим следующий пример функции с критической секцией:
// C++11
void foo() {
// ...
{
std::lock_guard<std::mutex> lock(m);
if (!container.empty()) {
// do something
}
}
// ...
}
Многим людям такая конструкция не нравилась, пустые скобки выглядят не очень красиво. Поэтому в C++17 решено было сделать всё красивее:
// C++17
void foo() {
// ...
if (std::lock_guard lock(m); !container.empty()) {
// do something
}
// ...
}
В приведенном выше примере переменная lock будет существовать до закрывающей фигурной скобки оператора if.
Structured bindings
std::set<int> s;
// ...
auto [it, ok] = s.insert(42);
// Теперь it — итератор на вставленный элемент; ok - bool переменная с результатом.
if (!ok) {
throw std::logic_error("42 is already in set");
}
s.insert(it, 43);
// ...
Structured bindings работает не только с std::pair или std::tuple, а с любыми структурами:
struct my_struct { std::string s; int i; };
my_struct my_function();
// ...
auto [str, integer] = my_function();
А ещё...
В C++17 так же есть:
- синтаксис наподобие template <auto I> struct my_class{ /*… */ };
- filesystem — классы и функции для кросплатформенной работы с файловой системой;
- std::to_chars/std::from_chars — методы для очень быстрых преобразований чисел в строки и строк в числа с использованием C локали;
- std::has_unique_object_representations <T> — type_trait, помогающий определять «уникальную-представимость» типа в бинарном виде;
- new для типов с alignment большим, чем стандартный;
- inline для переменных — если в разных единицах трансляции присутствует переменная с внешней линковкой с одним и тем же именем, то оставить и использовать только одну переменную (без inline будет ошибка линковки);
- std::not_fn коректно работающий с operator() const&, operator() && и т.д.;
- зафиксирован порядок выполнения некоторых операций. Например, если есть выражение, содержащее =, то сначала выполнится его правая часть, потом — левая;
- гарантированный copy elision;
- огромное количество математических функций;
- std::string::data(), возвращающий неконстантый char* (УРА!);
- constexpr для итераторов, std::array и вспомогательных функций (моя фишечка :);
- явная пометка старья типа std::iterator, std::is_literal_type, std::allocator<void>, std::get_temporary_buffer и т.д. как deprecated;
- удаление функций, принимающих аллокаторы из std::function;
- std::any — класс для хранения любых значений;
- std::optional — класс, хранящий определенное значение, либо флаг, что значения нет;
- fallthrough, nodiscard, maybe_unused;
- constexpr лямбды;
- лямбды с [*this]( /*… */ ){ /*… */ };
- полиморфные алокаторы — type-erased алокаторы, отличное решение, чтобы передавать свои алокаторы в чужие библиотеки;
- lock_guard, работающий сразу со множеством мьютексов;
- многое другое.
Напоследок
На конференции C++Siberia в августе мы постараемся рассказать о новинках С++ с большим количеством примеров, объяснить, почему именно такой дизайн был выбран, ответим на ваши вопросы и упомянем множество других мелочей, которые невозможно поместить в статью.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Комментарии (394)
antoshkka
13.07.2016 20:04+3Модули уже почти готовы (по крайней мере дело дошло до wording — «патча» к существующему стандарту). Если не будет найдено фатальных недостатков, то есть все шансы увидеть модули в C++Next.
mezastel
13.07.2016 20:57+5Да, но это еще минимум до 2020 ждать. Формально. Я конечно надеюсь что хотя бы Microsoft сделают их побыстрее (уже есть подвижки), но хочется везде и немедленно. И в связи с этим куча всяких вопрос: например, сможем ли мы выкинуть на помойку CРР/HPP файлы и просто иметь CPP и всё? Потому что это уже надоело, и самое главное что по стандарту много чего нельзя делать header only, например специализации шаблонов.
Еще конечно хочется полностью новый STL. Текущая стандартная библиотека мне видится тупиком. Взять хотя быstd::string
— на CppNow, Sean Parent рассказывал что у них там 4 реализации строк в коде. А все почему? Потому что обычный API этой строки убогий, а выкинуть и сказать «мы накосячили, давайте заново» никто не готов.antoshkka
13.07.2016 21:57+1Еще конечно хочется полностью новый STL
В Оулу решено было забронировать все namespace stdцифры для будущего использования стандартной библиотекой. Тоесть когда-нибудь и правда может появиться std2::. У людей в комитете уже сейчас есть идеи для новой версии стандартной библиотеки. Но вот появится она наврядли в ближайшие 10 лет.
zelyony
13.07.2016 22:16+3да уж, 30 лет не могут сделать нормальные строчки — поиск, замена, lower/upper/locale
30 лет «ходим на костылях», а комитет грезит о дальнем космосе
запилить строки, синхронизацию, работу с файлами и сетью (+ асинки) — закроет 95% потребностей программистов
одну либку возмешь, она зависит от boost::asio, другая с необходимыми вещами, что нет в первой — от libevent… кто-то еще притянул ProtoBuf, а еще один решил, что для разных клиентов будет удобнее Thrift(с их сервисами и транспортами)… в итоге в приложении 3-4 либки работы с сетью (неплохо бы еще ZeroMQ с подписками и RabbitMQ для message-queue), 2-3 вида строк и 5-6 реализаций мьютексов и умных указателей… рукалицо/больпечаль
что касается новой STL из-за строк, то это ты погорячился: достаточно одобрить extension methods и впилить icu-либку, уже будет счастье:
string toLower( string_view sv ) {… }
auto str = string(«Hello WORLD!»).toLower();
offtopic: конечно в узкоспециализированных вещах С++ замены нет, но НЕ для «железного» уровня рассматриваю C++ как низкоуровневую(нативную, шуструю) реализацию некоторых вещей для более удобной/человеческой(созданной для людей) среды, что-то вроде С++ — это интринсики для C#/Java (еще бы их interop был гладкий… мечты)VioletGiraffe
13.07.2016 22:23+3Чёрт бы с поиском-заменой, сами напишем. Дайте поддержку UTF-8 строк! Именно строк как набора символов, а не байт. ЧТоб итерирование работало посимвольно и т. д.
antoshkka
13.07.2016 22:36+2Есть годные на мой взгляд предложения, но до них не дошли руки и в C++17 их не будет :(
monah_tuk
14.07.2016 13:29+1Можно ссылки? Я волшебное слово знаю —
ДАЙ!пожалуйста!antoshkka
14.07.2016 14:47+1Самое толковое на сегодняшний момент http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0244r1.html.
Rezzet
14.07.2016 00:49+1Поддержка кодировок из коробки — рука лицо, неужели так сложно сделать конвертацию разных кодировок из коробки, и да поддерживаю все что сказано вышел про сеть, строки, я бы ещё рефлекшен попросил( вполне устроили бы флаги компилятора) и аналог protobuff/Thrift, локализацию, стандартные либы типа zlib, libpng, libjpg из коробки, xml парсеры, математические типы, хотябы float3, matrix4x4, quaternion и операции с ними. Мультиметоды ещё хочу, пусть медленные ну да и черт со скоростью.
Берешь графический движок, в нем Vector3, берешь физически движок в нет Point3, берешь звуковой в нем float3, берешь либу с поиском пути в ней Float3 и сидишь пишешь бесконечные конвертации точек и матриц из одной либы в другую, со строками такая же бида.TargetSan
14.07.2016 00:56+10Cargo. Просто Cargo. И все эти 3 либы будут использовать один набор типов из крейта 3d-geometry.
Упс, мы же про С++. Нет, не судьба.
Qbit
19.07.2016 14:52> Берешь графический движок, в нем Vector3, берешь физически движок в нет Point3
Строго говоря, Vector и Point — это разные типы данных (даже если одинаковые структуры данных).
VoidEx
14.07.2016 01:52+2> посимвольно
по-code-point-но вы имеете в виду?khim
14.07.2016 02:41+6Я думаю он имеет в виду «шоб с русским работало, а все немцы и турки идут в жопу».
Но это мысли — интересно в какие слова они облекутся.webkumo
14.07.2016 11:20Та же Java умеет и так и так. Хочешь — итерируешься по символам (кодпоинты идут… далеко идут), хочешь — по кодпоинтам. В чём трабла сделать аналогично в c++?
PS просто не всегда нужно умляуты и прочие радости учитывать при обходе строки (например, если ищешь цифры — то они никакого значения не имеют).khim
14.07.2016 13:28+3PS просто не всегда нужно умляуты и прочие радости учитывать при обходе строки (например, если ищешь цифры — то они никакого значения не имеют).
Если ищешь цифры (парсишь XML или там JSON), то достаточно работать с байтами если у тебя UTF-8.webkumo
14.07.2016 13:47Цифры — лишь пример. Вопрос в том, что умляуты и прочие код-поинты, не учитывающиеся как символ для некоторого класса задач не имеют значения. Да и кодировка может быть не одно-четырёх-байтовая UTF-8, а двубайтовый UTF-16.
PS под возможной кодировкой я подразумеваю, что для хранения данных в типе строки кодировка (по уму) должна быть одна, но её можно выбирать из разных кодировок, в то же время я знаю две универсальные кодировки — UTF-8 и UTF-16.DistortNeo
14.07.2016 14:31+1Итерация по символам юникода с полноценной поддержкой большого числа правил ресурсозатратна как по скорости обработки, так и по памяти.
Тут всё зависит от задачи. Если задача — просто распарсить JSON, то работа со строкой будет осуществляться как с обычным массивом с использованием небольшого числа строковых функций. В случае программирования микроконтроллера полноценная поддержка юникода тоже явно будет лишней.
Я бы вообще не изобретал велосипед, а пользовался исключительно средствами операционной системы. В STL включил бы только врапперы для их вызова. Для основных задач обработки строк этого достаточно. Ну а что касается поддержки экзотических случаев — не стоит ими засорять стандартную библиотеку.gecube
17.07.2016 13:24Микроконтроллер и С++ 17-года? Ну-ны.
Мне кажется, что все-таки в МК используют либо свой диалект Си, либо некое подмножество современного С++.
Но то что язык идет в тупик — это факт.
(С++ реально крутой, но каждая итерация нового стандарта делает его все монструознее и монструознее)Falstaff
17.07.2016 13:55+3Проблема в том, что никто не будет писать компилятор именно для какого-то подмножества, да и кто его будет стандартизировать. Поэтому я согласен с DistortNeo в том что раздутая стандартная библиотека имеет минусы — на маленьких контроллерах используется тот же компилятор и тот же язык (разве что системные вызовы из libc и иже с ним по необходимости приходится самостоятельно делать), и C++ там тоже очень полезно использовать, но по мере роста библиотеки, во-первых, будет усложняться портирование, а во-вторых будут учащаться случаи, когда хочется использовать вполне скромный стандартный класс, а он за собой дёрнет iostream — бабах, плюс двести килобайт кода, а на контроллере всего сто двадцать восемь. Языку очень-очень нужны модули и что-то вроде пакетного менеджера, вроде того что у Rust — лучше сподвигнуть людей писать больше лёгких в использовании библиотек, чем засовывать в комплект ещё больше батареек.
khim
17.07.2016 23:26+2Языку очень-очень нужны модули и что-то вроде пакетного менеджера, вроде того что у Rust — лучше сподвигнуть людей писать больше лёгких в использовании библиотек, чем засовывать в комплект ещё больше батареек.
«Батарейки в комплекте» — штука тоже хорошая, но тут у C++ тоже огромная засада: так как каждый тянет одеяло на себя, то в результате в стандарте — очень странная комбинация из вещей, которые мало кому нужны (но зато против которых никто не возражал) и полнейшее отсуствие вещей, которые всем нужны, но про которые каждый хочет чего-то своего.
Простейший пример: ни запустить подпроцесс, ни разобрать параметры, пришедшие вmain
(с выделеним обязательных/необязательных параметров, etc) средствами стандартной бибилиотки нельзя. Разработчикам систем для микроконтроллеров это, в общем-то и не нужно — но зато нужно всем остальным!Antervis
18.07.2016 05:30std::system запускает подпроцесс. А разбирать параметры, пришедшие в main, можно далеко не единственным способом (как минимум, синтаксис параметров в винде и линуксах разный). И пусть лучше этот парсинг остается в виде отдельных либ, типа boost::program_options
khim
18.07.2016 16:29+5std::system запускает подпроцесс.
Лучше бы он его не запускал. Количество дыр в безопасности, которые образовались из-за того, что в стандарте есть только std::system — не счесть.
Но да, запускает, тут вы правы.
А разбирать параметры, пришедшие в main, можно далеко не единственным способом (как минимум, синтаксис параметров в винде и линуксах разный).
С одной стороны — вы правы: для создания переносимых программ какой-нибудь getopt не годится.
С другой — это значит что сотни тысяч программистов пишут сотни тысяч велосипедов при написании простейших утилит (потому что требование «использовать только стандартуную библиотеку» у заказчиков встречается частенько). Зачем? Ради чего вся эта бурная деятельность?Antervis
18.07.2016 17:59+1Представьте себя на месте члена комитета. Вам предлагают стандартизировать реализацию фичи, для которой отсутствуют даже общие соглашения о том, как она должна работать.
khim
18.07.2016 18:41+5А вы представьте себя на месте пользователя, которому нужно что-то простенькое написать — и которого мало волнует наличие «общих соглашений» вокруг фичи.
Я прекрасно понимаю почему члены комитета не могут решить эту проблему — но это не меняет того факта, что конечный результат (огромный и очень сложный язык, который, тем не менее «из коробки» не умеет «элементарных вещей») — очень неприятен.
0xd34df00d
20.07.2016 01:18+2потому что требование «использовать только стандартуную библиотеку» у заказчиков встречается частенько
Я бы на месте этих программистов старался избегать таких заказчиков с такими немотивированными требованиями.khim
20.07.2016 17:58+2Требование как раз вполне разумное: код, который не тянет за собой внешних библиотек, легко интегруется куда угодно, код написанный поверх разных сторонних библиотек зачастую интегрировать — та ещё беда.
Никогда не пробовали объединять код, написанный даже не на основе разных библиотек, а на основе всего-навсего всем известного boot'а? Когда один компонент хочет версию 1.20, а другой 1.50… подружить их бывает ох как непросто.0xd34df00d
21.07.2016 21:43+1Опыт работы с тем же бустом говорит, что для обновления с 1.20 на что-то более новое чаще всего требуются весьма минимальные изменения.
В худшем случае вы всегда успеете переписать с буста, getopt или чего там ещё на что-то своё. Зато вы сейчас сэкономите больше времени, и сможете его потратить на проработку действительно важных вещей в вашем приложении, а не изобретение ещё одного костыля.
Вдруг не взлетит, и всё равно выкидывать придётся.
0xd34df00d
18.07.2016 04:35+4Микроконтроллер и С++ 17-года? Ну-ны.
gcc вроде до сих пор всякие там atmega и прочие attiny поддерживает, разве нет?
khim
14.07.2016 14:52+3Вопрос в том, что умляуты и прочие код-поинты, не учитывающиеся как символ для некоторого класса задач не имеют значения.
Вот я и прошу пример «класса задач». Пока всё, что я видел распадается на два класса: либо оно нормально работает с UTF-8 с побайтовой адресацией, либо адресация по кодпоинтам задачу не решает, а просто загоняет проблему вглубь. Как я написал выше: «шоб с русским работало, а все немцы и турки идут в жопу». Я, в общем, понимаю почему такой подход международный коммитет по стандартизации не очень хочет поддерживать.
PS под возможной кодировкой я подразумеваю, что для хранения данных в типе строки кодировка (по уму) должна быть одна, но её можно выбирать из разных кодировок, в то же время я знаю две универсальные кодировки — UTF-8 и UTF-16.
И опять-таки дихотомия неправильная. Есть два класса кодировок:
1. UTF-8 — кодировка с которой просто работать и расширять языки не нужно
2. Всё остальные кодировки — работать с которыми сложно и потому расширять язык тоже не нужно
UTF-16 относится ко второму классу. UCS-2 (из-за которой в некоторых языках и операционных системах и появилось угробище под названием UTF-16) — да, была проста и красива. Хотя платила за это наличием своих собственных недостатков. UTF-16, увы, имеет все недостатки UTF-8 (один кодпоинт и один элемент строки — разные вещи), но также унаследовала все недостатки USC-2 (например бывает UTF-16LE и UTF-16BE, а это значит что вам нужен BOM и вам нужно знать есть у вас BOM или нет и т.д. и т.п.)
Использовать её нужно только там где есть внешний API, требующий её использования — но наличие внешнего API автоматически обозначает что есть и процедуры для работы с этим творением человеческого разума, а значит иметь API в языке — уже не нужно.mayorovp
14.07.2016 16:58Вообще-то, в UTF-8 тоже есть BOM...
stack_trace
14.07.2016 17:00Гм, можно ссылку? И объяснение, зачем он нужен, заодно?
mayorovp
14.07.2016 17:11Я так понимаю, он выступает в качестве сигнатуры кодировки. Ссылку не найду.
Но в том же C# любой текстовый файл, записанный в кодировке UTF-8 по умолчанию будет иметь BOM.
stack_trace
14.07.2016 17:14+1Просто BOM — по определению Byte Order Marker. В UTF-8 никакого byte order нет и быть не может, так как единица анализа в нём — один байт. Возможно, очередное гениальное решение Майкрософта?
RPG18
14.07.2016 17:32+2stack_trace
14.07.2016 22:19+1Это размер код поинта, а единица анализа — один байт, и следуют он точно один за другим в одном порядке на любой архитектуре. Проще говоря, парсится UTF-8 побайтово, в отличие от того же UTF-16.
khim
14.07.2016 18:52Именно так. Майкрософт решил его использовать как маркет UTF-8 файла. Но это уже самодеятельность — мало что что ещё можно придумать чтобы жизнь себе усложнить.
VioletGiraffe
14.07.2016 07:51+3Да, именно так. Чтоб была какая-нибудь функция length, которая возвращает количество code points, и какой-нибудь operator[], который возвращает собственно code point.
khim
14.07.2016 13:29+2Как где-то писал Страустру: «кажется глупым добавлять в язык конструкцию, чуть ли не единственное назначение которой — порождение ошибок определённого класса».
khim
14.07.2016 02:40Именно строк как набора символов, а не байт.
Можете привести хоть одну задачу, где это реально нужно?
Я знаю много задач где подобный подход породит проблемы (то есть: оно будет работать в простых случаях, но рассыпется нафиг в сложных) и не знаю ни одной практической задачи, где бы это помогало.
P.S. Так, навскидку: не забываем что upcase("?") == «SS», что upcase(«i») ==«I» у большинства народов, но турков upcase(«i») == "I" и т.д. и т.п.VioletGiraffe
14.07.2016 08:06Это реально нужно в любой задаче, где нужно что угодно делать с не-ASCII текстом. Распарсить XML, например.
VoidEx
14.07.2016 09:08+3Зачем там кодпойнты? Чтобы найти '<', '>', '/', кавычки — про код-пойнты знать не надо, работы «как с ASCII» хватит, так как кусок кодпойнта принять за '<' невозможно. Чтобы понять, пробельный ли символ — тоже. Чтобы сравнить открывающий тег с закрывающим — тоже. Что я не учёл?
Azoh
14.07.2016 11:44Например того, что существуют многобайтные кодировки.
khim
14.07.2016 14:56Хватит фигнёй страдать, а? Вы мне реализуйте прототип строки с осмысленным
operator[]
для ISO-2022, а я посмеюсь. Только про эскейп-символы и связанные с ними радости не забудьте, пожалуйста.Azoh
14.07.2016 15:23Вам для парсинга нужен доступ по произвольному смещению? Если не секрет, то зачем?
khim
14.07.2016 18:58Это то с чего началась данная ветка: дайте, типа, нам посимвольный доступ в UTF-8 строку.
На вопрос «зачем» ответа так и не последовало, хотя, я на 99% уверен в своей версии: «нам с русским языком работать не удобно, а о немцах, турках и японцах мы думать не хотим — пусть идут куда им захочется… хотят на три буквы, хотят — на пять или десять».
Как вы понимаете такая мотивировка в международном комитете по стандартизации вряд ли получит движение.Azoh
15.07.2016 10:14+1Посимвольный произвольный доступ в UTF-8 идея довольно сложно реализуемая. Посимвольный последовательный доступ сделать относительно несложно. В задачах парсинга это как раз очень облегчает жизнь. Для реалистичной по скорости реализации произвольного доступа нужно либо использовать UTF-32, что бьет по памяти, либо UCS-2, но она не покрывает весь юникод.
По хорошему, строки и символы должны быть отделены от массивов, байтов и кодировок. И уже разработчик будет решать, что ему использовать. Нужны что-то более сложное — вот тебе полноценные строки, но за них придется платить памятью и скоростью. Хотя юникод и поддержка многих языков в любом случае не будет бесплатной.khim
15.07.2016 20:45По хорошему, строки и символы должны быть отделены от массивов, байтов и кодировок.
А кто их отделять будет? Вопрос не праздный: Python3 решил пойти по этому пути, что привело к тому, что пользоваться этим ужасом просто невозможно.
И уже разработчик будет решать, что ему использовать.
Если бы всё было так просто. Проблема в том, что переход между «потоком байт» (ну, например, именем файла в Linux'е) и «текстом» (ну, например, сообщением «файл «...» имеет размер «...» байт») вовсе не так очевиден как хочется и кажется.
Нужны что-то более сложное — вот тебе полноценные строки, но за них придется платить памятью и скоростью.
А что будет если вы отнесёте что-то не в ту категорию? Программисты очень часто не задумывась, относят что-то не в ту категорию — и если у вас есть два жёстко разделённых мира, то исправление этой ошибки — очень дорого. Если у вас и то и другое — строки (подход C++, Go, Python2 и множества других языков) — то жить становится гораздо легче.
Стандартизация библиотеки для работы с текстом — дело хорошее, только, ради бога, не засовывайте это прямо в язык.
Idot
20.07.2016 14:26Это то с чего началась данная ветка: дайте, типа, нам посимвольный доступ в UTF-8 строку. На вопрос «зачем» ответа так и не последовало, хотя, я на 99% уверен в своей версии: «нам с русским языком работать не удобно, а о немцах, турках и японцах мы думать не хотим — пусть идут куда им захочется… хотят на три буквы, хотят — на пять или десять».
А японцы, которых Вы упомянули, по-Вашему, что UTF-8 — не используют?!
Это необходимо всем у кого алфавит не на латинице, то есть как минимум 3 миллиардам человек = китайцы + индийцы + японцы + корейцы + таиландцы + пакистанцы + бангладешцы + арабы + и так далее… То есть даже без России — это половина человечества, если не больше, так как в Африке существуют алфавиты тоже основанные не на латинице.Dionis_mgn
20.07.2016 14:56+3Человек убежден в том, что 99% страждущих посимвольный доступ к UTF-8 строкам хотят весьма кривую реализацию, которая будет отлично работать с кириллицей и не работать с иероглифами, немецким и турецким языками и т.п. Ибо эти 99% человек просто не сталкивались с этими проблемами, а потому не представляют, во что превратится нормальная поддержка UTF-8 в стандарте. И я с ним согласен, впихнуть UTF-8 в стандарт — это надо постараться. Да и кому оно ДЕЙСТВИТЕЛЬНО надо?
Я последние 3 года разрабатываю старый Web-проект с бэкендом на плюсах (+ свой проект тоже на плюсах). Ни разу не было реальной необходимости в посимвольном доступе к UTF-8 строкам.
В тех же случаях, когда сложная работа с UTF-8 действительно нужна, лучше просто взять специальную библиотеку, а не желать этого в стандарте языка.
Это примерно как желать 3D движок в стандарте C++. Но что-то я не вижу желающих интегрировать в стандарт, например, OGRE.DistortNeo
20.07.2016 16:53-3Стоп. Давайте разделять UTF-8 и Unicode.
UTF-8 — это просто способ кодирования 31-битных значений в виде последовательности 8-битных байт. Он никак не привязан к смысловому содержанию этих значений.
Для подавляющего большинства практических задач обработки строк посимвольный доступ (=преобразование UTF8 в UCS2/UCS4 на лету) не нужен — можно работать напрямую с UTF8 представлением строки.
Если всё-таки нужен произвольный посимвольный доступ, то будет гораздо эффективнее преобразовать строку в массив 2-байтных или 4-байтных значений, а после работы преобразовать обратно. А если ещё хочется и менять значения символов на месте, то как вы себе это представляете для UTF8?
Idot
21.07.2016 07:17Ну, работа с текстом требуется куда чаще, чем работа с 3D. Давно уже большая часть приложений не сводится к «продвинутому фортрану».
VioletGiraffe
14.07.2016 13:39Да, вы правы, если в строке интересны только ASCII символы — можно обойтись без понимая UTF-8 и Юникода вообще.
Но вот мне, например, недавно нужно было искать в тексте кавычки не только обычные, но и “/”, «/», а также символ троеточия.khim
14.07.2016 14:43+3Символ троеточия ищется в UTF-8 как последовательность трёх байт 0xe2 0x80 0xa6 — для этого совершенно не нужны глубины глубин.
Только сначала прогоните текст один раз через фильтр, убедитесь что это корректный UTF-8…
warlock13
14.07.2016 11:43+2Если вы пишете текстовый редактор, вам скорее всего потребуется разбивать текст на слова, причём разделителями, как вы понимаете, могут служить не только ASCII-пробелы.
Или если вы пишете компилятор для языка программирования, поддерживающего юникодные спецсимволы (ну как в Mathematica и Haskell), то вам тоже понадобится посимвольный разбор.
khim
14.07.2016 14:37+2Если вы пишете текстовый редактор, вам скорее всего потребуется разбивать текст на слова, причём разделителями, как вы понимаете, могут служить не только ASCII-пробелы.
А ещё их может не быть — совсем. Как в Китайском и Японском, к примеру. И вам всё равно нужно знать на каком языке текст и нужна отдельная процедура, которая этим занимается.
Или если вы пишете компилятор для языка программирования, поддерживающего юникодные спецсимволы (ну как в Mathematica и Haskell), то вам тоже понадобится посимвольный разбор.
Зачем? Если вам достаточно «халтурной» реализации — посимвольной обработки хватит. А если вы хотите полноценную поддержку, то тут всё сложнее. Нужно как-то обрабатывать LTR, возможно придётся как-то озаботиться сравнением Hangul Syllables и Hangul Jamo и прочее.warlock13
15.07.2016 10:20А ещё их может не быть — совсем. Как в Китайском и Японском, к примеру.
Не понял, чего нет в китайском и японском? Пробелов?
И вам всё равно нужно знать на каком языке текст и нужна отдельная процедура, которая этим занимается.
Ну в этой-то отдельной процедуре настоящие юникодные строки вам будут нужны, так ведь?
khim
15.07.2016 20:49+1Не понял, чего нет в китайском и японском? Пробелов?
Угу. Для того, чтобы понять где можно переносить строку нужно использовать достаточно сложный алгоритм, содержащий, среди прочего, ещё и словарь иероглифов…
Ну в этой-то отдельной процедуре настоящие юникодные строки вам будут нужны, так ведь?
Скорее нет, чем да. Оперировать с отдельными символами вам там нужно будет очень редко, вполне достаточно требования что на входе — корректная UTF-8 строка. Вот это — да, нужно чётко отделять уровни где у вас std::string — это ещё «поток байт» и уровни где это уже текст (==корректный UTF-8).
mezastel
13.07.2016 22:41-5Со строками например такая проблема: у строки есть и
size()
иlength()
. Вот представьте вы начинающий разработчик, хотите понять что это за функции такие? Интуиция как бы подсказывает, чтоlength
= количество буков (символов), аsize
= размер "сколько вешать в байтах". Но не тут-то было. К тому же, в упор непонятно, почему этоlength()
— функция, а не свойство строки. Но это уже другая история.
Про UTF-8 я вообще помалчиваю. Мне бы хоть ASCII но с вменяемым синтаксисом. Где
split(), to_lower()
, и прочие очевидные вещи? Почему я использую Boost на каждый чих? Ведь Boost — это глобальные функции, а это полная отсутствие discoverability, то есть, есть у вас IDE, нет ее, не важно, т.к. комплишн по всем глобальным символам еще-не-заимпортированных заголовков не сделать в принципе.
Экстеншн-функции — хорошая, проверенная в C# тема, но с ней тоже как-то не торопятся. А даже если сделают, мы что, будем стандартизировать эти "патчи" стандартным типам? Или каждый тихо в гараже запилит свой
split()
и его будет шипить как сорцы?
Вообщем, проблем много.
warlock13
14.07.2016 02:27+16К тому же, в упор непонятно, почему это length() — функция, а не свойство строки.
Потому что в C++ нет свойств? Или я что-то пропустил?
JIghtuse
14.07.2016 07:28+1"Стандартный" split должен быть эффективен и широко применим. Есть n3593 с довольно адекватной мотивацией. Ждёт как раз упоминаемых string_view и Ranges.
stack_trace
14.07.2016 17:00+3Народ просто, похоже, не понимает, что большинство этих «ненужных» фич принимается именно для того, чтобы была возможность нормально имплементировать широко используемые фичи в стандартной библиотеке. Например, нормальные умные указатели были невозможно без мув-семантики, туплы — без вариадик темплейтов. Отсутствие же вариадик темплейтов не позволило имплементировать аналог printf-а, что привело к возникновению стримов — единственного возможного решения для типизированного ввода-вывода в таких условиях.
Videoman
14.07.2016 17:44Понимают, но оптимизация в прикладном коде, к сожалению, не возникнет сама собой и везде. Выходит для того, чтобы пользовательский класс стал «эффективным» в рамках STL, народу также нужно разбираться во всех тонкостях и особенностях реализации стандартной библиотеки, а это, учитывая размер разбухающего стандарта, уже слишком, на мой взгляд…
Idot
14.07.2016 04:31-530 лет не могут сделать нормальные строчки — поиск, замена, lower/upper/locale
30 лет «ходим на костылях», а комитет грезит о дальнем космосе
запилить строки, синхронизацию, работу с файлами и сетью (+ асинки) — закроет 95% потребностей программистов
Точно вместо удовлетворения необходимостей, комитет занимается тем что блаженно чешет своё ЧСВ. :(
PS создайте кто-нибудь петицию на Change.org чтобы до этих зажравшихся козлов в комитете наконец дошло!
DistortNeo
13.07.2016 23:16+1Модули — это основная причина, делающая крайне затруднительным использование стороннего кода и невозможным — менеджеров пакетов. Кстати, экспериментальная реализация модулей уже есть в Visual Studio 2015 Update 2. Но она настолько сырая, что пользоваться ею не хочется совсем.
STL ужасен до невозможности после опыта работы с более новыми языками с нормальными библиотеками. Больше всего бесит широкое использование беззнаковых типов и смешение знаковых/беззнаковых. Затем — ужасный iostream с нелогичными перегрузками и форматированием. В качестве прикола пытался написать аналог .NET String на C++ с SubString (с указателями), особенно String.Format с variadic templates. Даже работало и было на порядок удобнее printf и тем более cout.DistortNeo
13.07.2016 23:49Хм, имелось в виду «отсутствие модулей» — плохо.
И я очень надеюсь, что с введением модулей будет полностью переработана STL.mezastel
14.07.2016 00:24Я вот думаю, так под шумок модулей можно и STL переписать. Но это скорее мечты.
Deosis
14.07.2016 10:37+4Основная проблема в том, что одним из столпов С++ является обратная совместимость: если кто-то 10 лет назад использовал какую-либо функцию, то её уже не удалить из стандарта. А превращать язык в снежный ком никто не хочет.
dpantele
14.07.2016 18:08Есть же примеры такие как auto_ptr и не COW-строк в последних stdlibc++
khim
14.07.2016 19:07auto_ptr
до сих пор поддерживается, а не COW-строки в GCC начали разрабатываться за много лет до появления стандарта (оказалось что COW-строки на многопроцессорных системах сильно медленнее, а экономия памяти редко когда стреляет).
Если вы не обнаружилось что из распространённых компиляторов только один использует COW-строки, да и тот планирует «при удобном случае» от них отказаться — фиг бы чего вышло. Да и то: это изменение старые программы не поломало, максимум — изменило потребление памяти.dpantele
14.07.2016 19:19И когда-то auto_ptr из стандарта совсем уберут, как убирают std::binary_function из c++17, то есть при указании -std=c++17 он не будет определён.
Реализацию, наверное, не template-функций убрать из библотеки будет сложно, но никто в приципе не мешает старым программам требовать старую stdlib.
А насчёт COW — это я имел ввиду то, что бинарную совместимость тоже иногда ломают.khim
14.07.2016 23:16А насчёт COW — это я имел ввиду то, что бинарную совместимость тоже иногда ломают.
А тут как раз очень забавно. У Microsoft'а она вообще ломается каждую версию, а в Linux'е введение не-COW строк, строго говоря, бинарную совместимость не сломало. Тeперь в стандартной библиотеке есть std::string и std::__cxx11:string — и, в теории, ничто не мешает библиотекам и программам продолжать поддерживать и использовать оба вида строк…dpantele
14.07.2016 23:24Ну нет, всё-таки одновременно два типа строк смешивать не получится нормлаьно, там же сигнатуры разные. Только если через c-интерфейс.
Но вообще вся моя идея была в том что функдамент миграции на новые бинарные и не совсем реализации заложен и вполне себе используется, так что я не очень удивлюсь если когда-то будут совсем выкидывать старые куски, требуя использовать старые компиляторы/библиотеки если что не так.
А сейчас нет чего-нибудь что требует страый gcc для сборки?khim
15.07.2016 21:02А сейчас нет чего-нибудь что требует страый gcc для сборки?
Старые программы испольщующие, скажем,iostream.h
? Это, кажется, самое заметное несовместимое изменение…
Предыдущие «циклы» (переход с GCC 2.95 на GCC 3 и с GCC 3 на GCC 3.4+), кажется все уже и забыли…
phprus
15.07.2016 11:03> а в Linux'е введение не-COW строк, строго говоря, бинарную совместимость не сломало.
Строго говоря как раз таки сломало.
Если разные модули будут использовать в своих API stl строки, и будут собраны в режиме С++11, но один компилятором GCC-4.*, а второй — GCC-5 и выше, то они будут ABI не совместимы.khim
15.07.2016 20:52Если разные модули будут использовать в своих API stl строки, и будут собраны в режиме С++11, но один компилятором GCC-4.*, а второй — GCC-5 и выше, то они будут ABI не совместимы.
Зависит от того — как именно вы используете GCC-5. Вы можете им собрать свою библиотеку сstd::string
, сstd::__cxx11:string
и даже с обоими вариантами одновременно.phprus
15.07.2016 21:14+2> Зависит от того — как именно вы используете GCC-5
В общем случае нет, не зависит.
Собрать то свою библиотеку я действительно могу как угодно, но если какая-либо библиотека уже собрана (например, компонент без исходников) пусть в gcc-4.9, а в дистрибутиве линукса установлен gcc-5+ (с новым ABI по умолчанию), то эта библиотека будет не совместима с поставляемыми в дистрибутиве другими библиотеками (они то используют std::__cxx11:string вместо std::string).
По Вашей ссылке об этой проблеме тоже написано:
> If the third-party library cannot be rebuilt with the new ABI then you will need to recompile your code with the old ABI.
А «your code» вполне может быть и энное количество поставляемых в системе библиотек.
Какая же это совместимость ABI, если требуется «recompile your code with the old ABI»?
encyclopedist
14.07.2016 16:04+1В Clang модули реализованы уже несколько лет (правда не совсем тот вариант что пошёл в стандарт).
iassasin
13.07.2016 22:26Рад слышать! Тоже очень жду модулей!
А где можно почитать «почти готовый» стандарт про них? Интересно, как он выглядит на данный момент.antoshkka
13.07.2016 22:32+1В публичном доступе находятся не самые свежие версии, но разница с последними ревизиями незначительна:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0142r0.pdf
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0143r2.pdf
NeoCode
13.07.2016 20:09+5Мне все интересно! Пишите больше, это очень интересная тема.
Некоторые мысли по статье
1. Как «if (init; condition)» будет взаимодействовать с объявлением переменных внутри круглых скобок if, т.е. «if(int x = foo())»?
Кстати, сама по себе форма уже очень близка вот к этому https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html, т.е. к тому чтобы сделать стейтменты выражениями, как в Scala или Nemerle.
2. Structured bindings… получается очень забавно — в С++ со всех сторон подошли к прямой реализации кортежей средствами языка, но видимо из-за «legacy» синтаксических особенностей так и не сделают. А так почти все есть — и списки инициализации, и pair/tuple, и вот теперь structured bindings…antoshkka
13.07.2016 20:161. Как «if (init; condition)» будет взаимодействовать с объявлением переменных внутри круглых скобок if, т.е. «if(int x = foo())»?
Если нет `;` в `if`, то считается что в круглых скобках condition. Другими словами — работать будет так же, как сейчас.
2. Structured bindings… получается очень забавно — в С++ со всех сторон подошли к прямой реализации кортежей средствами языка, но видимо из-за «legacy» синтаксических особенностей так и не сделают. А так почти все есть — и списки инициализации, и pair/tuple, и вот теперь structured bindings…
Одно из предложений, которые обсуждали на собрании, как раз было что-то похожее на «кортежы средствами языка». Но это был скорее исследовательский доклад, и решено было пока продолжить исследования и посмотреть что получится.NeoCode
13.07.2016 20:44А если я там напишу более двух стейтментов? if( foo(); bar(); baz(); condition )
Интересны пределы возможностей, так как реально это очень похоже на возможность использования цепочки стейтментов в выражении.
Кстати, «if constexpr» это небось из D «static if» таки протащили?mezastel
13.07.2016 22:01+1Ну не удивительно, т.к. Александреску позиционировал static if как основное преимущество D.
gecube
17.07.2016 13:33static if кроме как для шаблонов где-то нужен?
mezastel
17.07.2016 13:36ну фактически static if заменяет некую функциональность как шаблонов так и макросов, т.е. может быть использован для условной компиляции и метапрограммирования.
Antervis
17.07.2016 13:56+1Компилятор выкинет тождественные if'ы даже если они не помечены как constexpr. Суть в том, чтобы подавленная ветка даже не компилировалась.
abikineev
14.07.2016 14:52+2Тем самым возможно иметь два различных объявления внутри if стэйтмента:
if (int x = foo(); double y = bar());
А также вместо инит стэймента может быть экспрешн стэйтмент:
if (do_something(); int x = foo());
Кстати, кланг уже поддерживает эту фичу (спасибо мне и Ричарду Смиту :)
stepanp
13.07.2016 21:15+3Видимо в бусте закончились фичи которые можно спереть в стандарт.
antoshkka
13.07.2016 22:00+1Фичей ещё очень много :)
Просто комитет не занят стандартизацией только библиотек Boost.stepanp
13.07.2016 22:12+11Непонятно чем он вообще занят. Импортов нет, концептов нет, filesystem нет. Даже pragma once(или ее аналог) не могут в язык внести.
stack_trace
14.07.2016 16:53+2Даже pragma once(или ее аналог) не могут в язык внести.
Может, потому что эту «фичу» невозможно корректно имплементировать? Уже сто раз оговорено, что pragma once не должна использоваться в нормальном коде.DarkEld3r
14.07.2016 16:57Можно ещё раз для тех, кто всё пропустил? Хотя бы ссылку. Потому что у нас pragma once используется и не вызывает никаких проблем, а кодовая база собирается разными компиляторами.
stack_trace
14.07.2016 17:06+2Если коротко, при использовании pragma once компилятор должен уметь корректно определять уникальность файла, что в общем случае невозможно из-за большого количества файловых систем и их особенностей. Самое простое, возможны ошибки при использовании символических или хард ссылок на один и тот же файл, которые могут генерироваться в процессе построения проекта и тп.
mayorovp
14.07.2016 17:17+1У систем сборки (того же make, к примеру) есть точно такая же проблема — они не могут различать артефакты (цели) по символическим ссылкам в общем случае.
Означает ли это, что системы сборки на основе артефактов не следует использовать? Нет, не означает. Это означает что всевозможные ссылки надо использовать осторожно.
И с pragma once то же самое.
khim
14.07.2016 19:09+1И с pragma once то же самое.
Нет, не то же самое. Систем сборки не страдающих от указанных вами проблем я не видел (для языков без модулей, в общем-то, и альтернатив не придумать), а дляpragma once
альтернатива есть, описана в стандарте и работает ничуть не хуже.ozkriff
15.07.2016 08:53Систем сборки не страдающих от указанных вами проблем я не видел
Хм, вроде как SCons по умолчанию использует md5 по содержимому файла для определения его уникальности. Вроде это должно помочь при сложностях со ссылками.
khim
15.07.2016 21:08Хм, вроде как SCons по умолчанию использует md5 по содержимому файла для определения его уникальности.
Если бы SCons этим занимался, то он бы обрабатывал мало-мальски сложные проекты невменяемо долго. MD5 Scons использует, но уже после проверок на изменение даты. Если файл изменится, а дата модификации и размер останутся неизменными — SCons ничего не заметит…
Всё просто: посмотреть на метаинформацию можно даже если у вас в проекте сотни тысяч файлов. А делать что-то другое — слишком накладно если у вас исходников достаточно много.ozkriff
15.07.2016 22:12+1Если бы SCons этим занимался, то он бы обрабатывал мало-мальски сложные проекты невменяемо долго
Это очень похоже на мое о нем впечатление)
Смотрю в доках:
By default, SCons keeps track of whether a file has changed based on an MD5 checksum of the file's contents, not the file's modification time.
И судя по тем же докам, проверка времени изменения перед проверкой хешей опциональна и требует явного включения —
Decider('MD5-timestamp')
.
DarkEld3r
14.07.2016 17:19Спасибо.
Из любопытства заглянул в исходники Clang: там и правда инклюд гарды используются. Тем не менее, все популярные компиляторы эту прагму поддерживают ведь. И я как-то не встречал рекомендаций от неё отказываться. Если она такая проблемная и это "общепризнанно", то могли бы хотя бы предупреждение выдавать.
Jebediah_Kerman
14.07.2016 18:08GCC первоначально выдавал предупреждение об отключении pragma once, если компилируемый код использовал её. Тем не менее, в релизе 3.4 GCC код обработки команды pragma once был исправлен для корректной работы с символьными и жёсткими ссылками. Данная возможность была сочтена полезной и предупреждение было убрано
https://ru.wikipedia.org/wiki/Pragma_onceDarkEld3r
14.07.2016 20:20+2В курсе, но результат как раз укрепляет меня во мнении, что проблемы с этой прагмой редкость.
stack_trace
14.07.2016 22:29+4Она не то чтобы очень проблемная, в большинстве случаев она работает так как надо. Вот только вносить в стандарт то что «в большинстве случаев работает так как надо» — плохое решение. К тому же сама возможность полностью корректной имплементации под вопросом, и при этом есть решение, лишённое недостатков pragma once. Поэтому отсутствие её в стандарте — закономерно и правильно, как по мне.
DarkEld3r
15.07.2016 10:46+1Справедливости ради, у решения "лишённого недостатков pragma once" есть свои недостатки, пусть и незначительные. Например, необходимость вручную следить за уникальностью имён, хотя заранее соглашусь, что это не особо сложно.
stack_trace
15.07.2016 11:15Ну, программисту за многим приходится следить самому — например, за уникальностью имён переменных, классов и функций, так что я не стал бы причислять это к серьёзным недостаткам.
DarkEld3r
15.07.2016 11:37+4Ну нет, вот за этим, как правило, как раз следить не надо (если не брать отдельные хитрые моменты): при создании одноимённого класса/функции компилятор честно выдаст ошибку.
Повторюсь — я не считаю, что проблема уникальности именования гардов сколь-нибудь остро стоит. Но лично мне всегда приятно когда за разными мелочами следит компилятор, а не самому требуется внимательность проявлять. По крайней мере, если автоматика не приносит свои особенности/недостатки.
khim
15.07.2016 21:15-1при создании одноимённого класса/функции компилятор честно выдаст ошибку.
При использовании одномённых гардов — тоже. Теоретически можно придумать код, который не вызоват ошибку при коллизии, а приведёт к неправильной работе — ну так и с именами функций такая фигня бывает.
Но лично мне всегда приятно когда за разными мелочами следит компилятор, а не самому требуется внимательность проявлять.
presubmit-скрипт решает это проблему…mayorovp
15.07.2016 22:28+2При использовании одномённых гардов компилятор ошибку, конечно, выдаст — но найти ее причину будет не проще чем найти
#define true false
. Потому что компилятор попросту не включит один из двух конфликтующих заголовочный файлов — и потом выдаст кучу воплей о необъявленных типах и функциях.0xd34df00d
16.07.2016 20:18+2Да. И с такими проблемами «в продакшене» я сталкивался, особенно при рефакторинге. С проблемами с прагмами я не сталкивался ни разу.
Bronx
15.07.2016 07:16Почему бы тогда не позволить #pragma once(MY_HEADER), которая была бы «сахаром» для
#ifndef MY_HEADER_#FILEHASH#
#define MY_HEADER_#FILEHASH#
…
#endif?khim
15.07.2016 21:20Потому что это решение — хуже существующего. Например потому что существующее решение не зависит от хеша файла.
P.S. Когда может оказаться полезным трактовать разные файлы как одинаковые? Когда это — разные версии одного и того же файла.
P.P.S. Я не говорю что существующее решение — идеально. Но так как все «простые решения» имеют свои подводные камни, то лучше не пытаться забить очередной костыль (который всего лишь заменит одну проблему другой), а всё-таки подумать на тему модулей. Ну не создают нормальные люди новые файлы так часто, чтобы эти три строчки были проблемой, не создают!DistortNeo
15.07.2016 23:18-1Генерите уникальный GUID на каждый новый файл. Всё, проблемы больше нет.
Mingun
16.07.2016 00:20+1Так проблема как раз в том, как определить, что файл новый. По хешу — долго, по таймстампу и имени — ненадежно.
TargetSan
13.07.2016 21:40+2inline для переменных — если в разных единицах трансляции присутствует переменная с внешней линковкой с одним и тем же именем, то оставить и использовать только одну переменную (без inline будет ошибка линковки);
Извините, но это капитальнейшая подстава и здоровенная пуха, которая теперь будет отстреливать ноги всем и по самую шею!
zelyony
13.07.2016 21:46+1сейчас линкер так работает для шаблонов. в чем будет заключаться подстава и не для шаблонов тоже?
иногда CPP-файл создается просто для того, чтобы в нем определить переменные… унылоTargetSan
13.07.2016 21:55+1Проблема в том, что можно схлопотать алиасинг на одну переменную там, где это не нужно. Для шаблонов это работает вынужденно.
Antervis
14.07.2016 06:15Чтобы так «пальнуться» надо написать что-то типа inline int a = ...; в двух .cpp вне классов/методов/неймспейсов. Нечаянно взломать сейф, так сказать
TargetSan
14.07.2016 09:05Не-а. Надо как раз забыть у статик-переменной дописать static, который сделает ей локальную видимость.
TargetSan
13.07.2016 21:46+1std::string_view
std::array_view я так понимаю не будет?
mezastel
14.07.2016 00:26-1Тут кстати интересная дуальность. Везде в STL у нас обобщенные алгоритмы которые берут
begin()/end()
не важно откуда. А для строк почему-то свойstring_view
хотя по логике вещей могли бы дать иarray_view
и сказать "а дальше вы сами". Ведь по сути, пока нет поддержки юникода, строки и массивы — это одно и то же :)TargetSan
14.07.2016 00:43+3Во-первых, у begin-end нельзя взять ни длину, ни подмассив указанного размера.
Во-вторых, при сшивании со старым кодом часто нужно передать пачку элементов непрерывным буфером. begin-end тоже пасуют.
По поводу же строк, у них есть ряд методов, которых нет у не-строковых срезов.
Ну и наконец, моё сугубо личное мнение, что сама по себе идея делать 2 итератора на диапазон, да ещё и требовать от них совместимости по интерфейсу с указателями, была отвратительной. Потому что реально в случае с RandomAccessIterator, например, у нас не 2 итератора, а слайс — убого скрытый под 2-мя итераторами. А теперь городим костыли в виде разнотипных итераторов. И, кстати, в stdlib до сих пор нет шаблонов для конструирования этих самых итераторов.
mezastel
14.07.2016 00:48Ну, если на то пошло, давайте признаемся что в современном мире более рационально сделать некий интерфейс
IEnumerable<T>
(привет C#) и поддержать ключевое словоyield
, которое дает возможность произвольно возвращать коллекцию даже тогда, когда это дерево и нужна рекурсия. В С++ сейчас писать итератор для дерева в рекурсивном стиле невозможно.
А потом можно передавать не
begin()/end()
а просто сам объект. И все счастливы.TargetSan
14.07.2016 01:00+8Примерно так и есть. Сделано, кстати, в одном языке на R, 4 буквы в названии. Ооочень удобно, даже без yield — когда итерирование по коллекции — 1 метод, а не как минимум 3-4.
TheIseAse
14.07.2016 09:48+3Что за язык? Ravascript? Rwift? Rython? Не томите!
TargetSan
14.07.2016 09:56+2Rust вообще-то. Почитайте на досуге, если интресно. Там, в частности, исправлены многие косяки С++.
TargetSan
14.07.2016 11:17+1А можно от несогласных пояснение, где я не прав? Или в споре о С++ другие языки и, в частности, потенциальных конкурентов, упоминать запрещено?
DarkEld3r
14.07.2016 15:06Ну… евангелизм бывает навязчивым. (:
Если что, мне самому раст очень интересен, так что минусов не ставил, даже наоборот.
khim
14.07.2016 15:10+2Потерпите пару годков. Go тоже несколько лет назад усиленно PRили. Потом успоколились. Сейчас вот Swift и Rust.
P.S. Swift меня не волнует от слова никак — точно так же, как не волнует, скажем, язык 1С (зачем мне язык намертво завязанный на чужую экосистему?), а на Rust — я поглядываю, но пока не решился ничего серьёзного на нём писать.DarkEld3r
14.07.2016 15:30+1Меня пиар раста не раздражает, просто понимаю людей, которые недовольны тем, что его рекламируют в темах про совсем другой язык.
TargetSan
14.07.2016 15:26+2Бывает. Впрочем, я не евангелист — так, сочувствующий. Стараюсь если вставлять про Rust, то по теме обсуждения и понемногу. И давайте наверное закрывать эту ветку, а то совсем оффтоп получается.
Antervis
14.07.2016 11:29есть функция std::distance, который как раз выдаст длину контейнера, определяемого итераторами begin и end. Точно так же у большинства контейнеров определен конструктор, принимающий итераторы и возвращающий, по факту, подмассив указанного размера: std::wstring ws = L«sometext»; std::string s(ws.begin()+4, ws.end()); вернет «text»
TargetSan
14.07.2016 11:44+1Проблема в том, что в общем случае очень трудно определить, какая категория итераторов пришла. Да просто нет возможности поставить ограничение «здесь принимается random access iterator».
Плюс, ограничение на тип элемента делается криво.
Плюс, функции, принимающие итератор, обязаны быть шаблонными — а принимающие срез конкретного типа — нет.
В общем, пользоваться можно — но срезы удобней.Antervis
14.07.2016 12:58+1Почему же трудно то?
template std::enable_if_t<std::is_same<std::random_access_iterator_tag, typename T::iterator_category>::value,void>
doSomething(T begin, T end) { /*...*/ }
Вот вам и ограничение «здесь принимается random access iterator».
Шаблонность в общем случае тоже необязательно — можно пользоваться auto функциями/лямбдамиTargetSan
14.07.2016 13:31+2Сравните с
void doSomething(stdext::slice<int> someslice) { ... }
- Нет enable_if_t. Который в большинстве случаев применяется как костыль в отсутствии концептов
- Нет шаблонности — можно имплементацию прятать в другой модуль.
- Банально легче читать
TargetSan
13.07.2016 21:48+2std::variant
Они таки вставили null state. Не очень хорошо, честно говоря. Я так понимаю, это жертва для обеспечения exception safety. Хотя могли бы потребовать noexcept для деструкторов и move конструкторов.
antoshkka
13.07.2016 22:14Они таки вставили null state.
Да, он называется valueless_by_exception() и возникает крайне редко.
Хотя могли бы потребовать noexcept для деструкторов и move конструкторов.
Есть много вариантов как реализовать std::variant, у всех есть свои недостатки. Мне нравилась версия «требовать noexcept default constructor для хотябы одного типа». Но у этой версии большие проблемы с юзабилити, так что std::variant с valueless_by_exception() намного более юзабельный.TargetSan
13.07.2016 22:45Не в курсе, к сожалению. Реализовывал свой велосипедик на эту тему, чтобы работал на GCC 4.9 и не аллоцировал память. В результате пришёл к двум ограничениям — noexcept destructor и noexcept move constructor. На их основе можно делать variant который или будет всегда в валидном состоянии, или уронит программу std::terminate. Оба ограничения как по мне вполне адекватные. Кидаться исключениями из деструктора нехорошо, а из мув-конструктора — просто глупо. Кстати, noexcept default ctor в таком случае не нужен.
encyclopedist
14.07.2016 02:46+1Проблема в том, что в обычной жизни есть довольно много типов без noexcept move коонструктора. Некоторые стандартные контейнеры например.
TargetSan
14.07.2016 09:06+3Вот кстати не могу представить throwing move. Подкинете пример?
antoshkka
14.07.2016 14:43Например один из вспомогательных компонентов boost::variant имеет потенциальный throwing move.
TargetSan
14.07.2016 14:57+1По-моему, аллокация памяти там сильно лишняя. Почему не могут просто отдать буфер? Ведь, по сути, recursive_wrapper почти то же самое, что и unique_ptr.
antoshkka
14.07.2016 15:22Если без неё, то становится намного хуже. recursive_wrapper отличается от unique_ptr тем, что не должен хранить nullptr. По смыслу recursive_wrapper — это ссылка, а не указатель.
Если разрешить ему хранить nullptr и кидать исключение при разименоввывании (а его разименоввывание происходит внутри variant и скрыто от пользователя), то variant теряет never-empty guarantee и иногда начинает хранить пустой recursive_wrapper. В итоге получается нечто среднее между boost::variant и std::variant, чего всячески хочется избежать.
Falstaff
13.07.2016 21:54+1Комбинация structured bindings и if (init; condition) очень вкусно смотрится:
if (auto [x, y] = foo(); y == something) {… }
Как я понимаю, переменные, объявленные в секции init, существуют и внутри else тоже?antoshkka
13.07.2016 22:17+3Да, внутри else тоже существуют. А ещё эта конструкция применима и к switch:
switch (auto [x, y] = foo(); y) { case 1: x += 10; break; // .... default: x = 0; }
kemm
14.07.2016 12:25+1Хм… А до такого когда-нибудь доведут, интересно:
switch (foo()) {
case [true, result]:
do_something(result);
break;
case [false, _]:
LOG(error)
kemm
14.07.2016 12:46+3Хм… А до такого когда-нибудь доведут, интересно:
switch (foo()) { case [true, result]: do_something(result); break; case [false, _]: LOG(error) << "Shit happens!"; break; }
Кстати, а плейсхолдеры добавили? Желательно что-нибудь минималистичное вроде того же "_", а не «std::structured_bindings::paceholders» 8)) И алиасы нподобие хаскеллевского «pair@(first, second)»? Понятно, что это сахарок, но без него таки не так сладко будет.
PS: прошу прощения, в прошлом комментарии что-то с форматированием стряслось…Antervis
14.07.2016 16:17+1так можно же писать using std::placeholders;
kemm
15.07.2016 14:38Ну тогда уж using _ = std::unused. И, к слову сказать, ещё вот какую-нибудь такую штуку было бы приятно иметь a-la designated inits:
auto [st_size: sz, st_mode: mode] = fstat_wrapper(fd); if (S_ISREG(mode) && sz > 0xfffffffful) std::cerr << "Ooops.\n";
jk057
15.07.2016 17:23А теперь хочется чего-то вроде
if ( auto [x, y, z] = [foo(), bar(), baz()]; y == something ) { }
encyclopedist
15.07.2016 17:31+2Так уже можно, используя std::tuple и новую возможность вывода аргументов шаблонов для классов:
if ( auto [x, y, z] = std::tuple(foo(), bar(), baz()); y == something ) { }
iyemelyanov
13.07.2016 21:54+7Очень интересно влияет ли развитие Rust на идеи участников комитета и обсуждается ли он? Да и вообще очень волнительно как один из любимых языков будет развиваться когда рядом есть такой конкурент как Rust, да еще учитывая новости о том, что его начали использовать в Dropbox, переписывать на нем отдельные части в Firefox и т.д. Просто раньше как то все было проще и понятней, был C и С++, и практически подавляющее большинство задач системного программирования решалось на них, но теперь то все эти задачи можно точно так же решать и на Rust с его вроде как передовыми идеями, и что дальше?
ababo
13.07.2016 22:14+19Безобразное усложнение С++ волей-неволей заставляет смотреть в сторону Rust.
mezastel
13.07.2016 22:16+2Судя по небольшому личному общению: они замечают тот уровень маркетинга, который обеспечивает себе Rust, но его фичи как таковые их не беспокоят т.к. большинство из них потребуют переделывания либо С++ либо стандартной библиотеки, а они не готовы ни к тому ни к другому.
stepanp
13.07.2016 22:19+2Думаю к моменту выхода C++20 никакой С++ будет уже не нужен.
VioletGiraffe
13.07.2016 22:29+9С тем же успехом можно сказать, что к моменту выхода С++20 уже никакой Rust будет не нужен.
Antervis
14.07.2016 06:30+5Шел 2196-й год… С++ праздновал свой двухсотый день рождения новым стандартом, а его всё хоронили и хоронили…
Error1024
14.07.2016 09:09+2Как дельфист: добро пожаловать!
snuk182
14.07.2016 10:38-2это уже впору называть каким-нибудь «синдромом флешплеера» — его убивают, а он все чето никак.
а Rust не будет конкурентом плюсам, пока к нему не будут разжеваны и задокументированы все шаблоны проектирования, иначе два из трех интересующихся будут отпадать в борьбе с компилятором.OnYourLips
14.07.2016 10:54+2Между «похоронили» и «популярен» есть еще много промежуточных состояний.
Они больше не популярны, количество вакансий снижается, почти не развиваются, сообщество растворяется.
Тот же флешплеер работает сейчас в лучшем случае на трети устройств.
Не вижу у этих технологий каких-то перспектив.
dendron
13.07.2016 23:41+7Что-то я уже не уверен что введение новых сущностей просто чтобы писать меньше строчек себя оправдывает.
Особенно неоднозначное отношение к string_view — как говорится если раньше было два способа написать функцию работающую со строками, то теперь их три. И каждый из них по-своему плохой.
Впитывая в себя куски из boost'а стандарт становится такой же беспорядочной помойкой — безумно распухшей коллекцией «прикольных фишечек».TargetSan
14.07.2016 00:25+9string_view это как раз очень хорошо — но чертовски поздно. Как и array_view. Надеюсь, к 20-му году прикрутят. А должны были вкрутить ещё в самом начале, в крайнем случае в 11-й версии. Но не судьба.
DarkEld3r
14.07.2016 15:14+2Ну string_view как раз штука удобная и нужная, да и раньше ей пользоваться можно было, что я успешно и делал. А вот вещи типа "if (init; condition)" тоже настораживают. Может это, конечно, во мне консервативность говорит, но оно кажется далеко не самым частым частным случаем. Для сравнения for по диапазону из C++11 мне очень нравится, да и случай чуть ли не самый частый.
Videoman
13.07.2016 23:46+9Какой кошмар!!! (Я в положительном смысле :). Интересно, а комитет изучал вопрос, сколько людей в состоянии изучить современный С++ с нуля, а не практикуя его постепенно в течении 20 лет?
Мне кажется, что пора уже заканчивать с этим и нужно садиться за написание книг, где подробно описывать как теперь все это использовать и самое главное, что не использовать из старых практик, пока они сами не забыли как правильно.mezastel
14.07.2016 00:29Это, вообщем-то, основная проблема. Сейчас мы можем втянуть студентов в С++ только силой, т.к. добровольно никто на нем писать не будет — сядут за C# или Java, сделают свое приложение, начнут его продавать. А в C++ остались те кто, как я, слишком долго сидел в этой теме и имеет некоторые априорные знания о том как все было 10-15 лет назад, и как проэволюционировало.
TargetSan
14.07.2016 00:53+9Потому что стандарт распух до неприличия — а многих действительно важных вещей нет до сих пор. А многих, таких как простого менеджера зависимостей и сборки, не будет никогда.
Поэтому С++ со временем выдавят. На это уйдёт куча времени, но его место займут другие языки. Что забавно, С, думаю, останется сильно дольше — т.к. он гораздо проще.mezastel
14.07.2016 00:56+1Слушайте, я вот недавно в Сан Фран ходил на встречу SG14, и там один человек на полном серьезе предлагал встроить С++ package management в язык (!!!). Хотя на дворе 2016 год и идея внешнего описания зависимостей уже проработана и испробована в Cargo, NuGet и так далее. То есть даже в C++ Working Group есть люди которые далеки от понимания того, что творится в мире разработки.
TargetSan
14.07.2016 01:06+2Это, простите, капец. По-моему, это PL/1 v2 уже какой-то.
Хотя, с другой стороны, заставить всех использовать единый формат пакетов и проектов можно только жёстко впилив его в стандарт. Но этого не будет никогда — потому что MS, Google, GCC team будут вечно тянуть одеяло на себя.
/fanboy mode on
Хочу только одну вещь. Поддержку в Rust'е импорт C headers из коробки. После этого "два крестика" покатится cо всё нарастающей скоростью.ozkriff
14.07.2016 13:03+3Хочу только одну вещь. Поддержку в Rust'е импорт C headers из коробки.
Мне кажется, усложнять язык для этого нет нужны, лучше доработать bindgen: https://github.com/crabtw/rust-bindgen#plugin
TargetSan
14.07.2016 13:27+1Там ЕМНИП самая большая проблема осталась — макро константы. Но не будем здесь разводить оффтоп.
sborisov
14.07.2016 14:23+2Когда Степанов выступал в «Яндексе» (видео доступно), он рассказывал про комитет, и говорил, что там очень много людей, которые просто за счёт компании едут посмотреть на Париж и другие места, где проходят заседания комитета. Дословно было сказано: "У них нет знаний, но зато есть мнение.", а работа комитета устроена так, что каждое предложение, нужно изучить и аргументированно утвердить или отказать, т.ч. бюрократия сильно мешает.
XaLBa
14.07.2016 01:47Так а что с ranges?
antoshkka
14.07.2016 14:49+1Они зависят от концептов. Сначала примут концепты, потом рейнджи. Когда примут концепты, и примут ли их — не известно.
Sliver
20.07.2016 03:45+1А как они зависят? На обычных шаблонах, кажется, без проблем делается.
Таскать везде begin и end вместо одного range — неудобно.
Тот же string view — это же, по сути, range, но почему-то только для строк?antoshkka
20.07.2016 14:17Ranges это не только добавление пары перегруженых методов к алгоритмам. Это еще и очень тесная интеграция с концептами и переписывание большей части снатдартной библиотеки. Так например метод all_of в данный момент выглядит вот так:
template<class InputIterator, class Predicate> bool all_of(InputIterator first, InputIterator last, Predicate pred);
С Ranges он будет выглядеть вот так:
template<InputIterator I, Sentinel<I> S, class Proj = identity, IndirectCallablePredicate<projected<I, Proj>> Pred> bool all_of(I first, S last, Pred pred, Proj proj = Proj{}); template<InputRange Rng, class Proj = identity, IndirectCallablePredicate<projected<iterator_t<Rng>, Proj>> Pred> bool all_of(Rng&& rng, Pred pred, Proj proj = Proj{});
Оценить масштаб изменений можно почитав proposal: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdfSliver
21.07.2016 03:16А это как-то мешает сейчас ввести ranges как простой шаблон с begin и end, а потом (если это потом настанет) уже расширить его концептами?
Переделок будет даже меньше, чем делают сейчас, например, с параллельными алгоритмами.JIghtuse
21.07.2016 07:06Одна из особенностей Range — begin и end могут быть разными типами (в примере выше это можно увидеть). Без этой возможности реализация значительно усложняется. А при её наличии ломается совместимость с предыдущими версиями стандартной библиотеки.
oYASo
14.07.2016 04:00+18Я большой фанат C++, но когда я читаю очередной черновик стандарта, мне всегда вспоминается этот комикс:
Да, есть хорошие и удобные фичи. Но дофига нужного до сих пор нет!
Навскидку:
1) нормальные, черт возьми, строки!
2) модули
3) полиморфные лямбды
4) xml, json и прочая сериализация/десериализация из коробки
5) в мире существует сеть, и пора бы уже о ней узнать!
6) ну и еще кучу всего, ночью уже сложно вспоминать…0xd34df00d
14.07.2016 04:11+3Что вы называете полиморфными лямбдами? А что — нормальными строками?
А вообще, а вообще… Лучше бы хорошую compile-time type information, чтобы можно было адекватно AST манипулировать, писать автоматический вывод той же сериализации хоть в json, хоть куда хочешь, и так далее.rzhikharevich
14.07.2016 21:18Что касается строк, есть интересная статья на эту тему: https://mortoray.com/2013/11/27/the-string-type-is-broken/
Sirikid
14.07.2016 04:13Да начнется священная война! (В какую статью про C++ не зайду обязательно кто-нибудь напишет такой список.)
mapron
14.07.2016 06:581) работа со строками это грусть, но это скорее претензии к STL, а не к языку.
2) ну все ждут и все хотят, но это такая глобальная тема, что комитет не хочет сделать какашку, от которой следующие 30-50 лет люди будут страдать.
3) а сейчас что не так
4) дайте рефлексию, и все это будет не нужно. А черновиков по рефлексии несколько уже есть.
5) вот тут просто +100500. Хотя бы ip-сокеты/listener-ы стандартизировали.monah_tuk
14.07.2016 13:535) тут можно много копий поломать. К примеру, проакторный Asio я бы не сильно хотел видеть в качестве реализации сети в стандарте. Плюс, говоря о сети, нужно думать и о эффективной работы, неблокирующем режиме и мультиплексировании… А вот тут некая система Windows выделяется своим IOCP (без спора — что эффективнее)...
antoshkka
14.07.2016 15:03+2Работа над сетью идёт, на данный момент самым жизнеспособным предложением является предложение на основе ASIO, но правиьно переработанное http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4575.pdf.
Очень много интересов сошлось на этом proposal. Все хотят сеть, но у всех разные желания: производительность, отзывчивость, простота, сопрограммы, единый интерфес с многопоточностью/stl, расширяемость и т.д.
Всей душой болею за это предложение и при том не я один. Должно получиться очень красиво и правильно, не не известно к какому году.
Sliver
20.07.2016 04:09+16*) нормальная функциональная поддержка для контейнеров чтобы можно было писать:
m.filter(...).map(...).reduce(...)
(но мы знаем, что это невозможно, пока не будет ranges, а ranges каким-то образом завязаны на концепты)
6**) Вдобавок к (непринятой в 17м стандарте) возможности вызывать метод класса как
method(object, arg)
, добавить аналог this для аргументов из C#, когда один из аргументов для функции можно написать через точку слева, как будто это вызов метода:arg1.func(arg2)
. Это позволит писать функции для работы с объектами, избегая огорода из скобок.
6***) Убрать неконсистентное требование того, что временные объекты обязаны быть константными — чтобы заработало вот такое:
func(C())
, гдеC()
— временный объект, который принимается в функцию по неконстантной ссылке.
Потому что сегодня приходится размазывать это на две строки:
C c = C(); func(c);
чему не видно никакого оправдания.antoshkka
20.07.2016 14:276**) Вдобавок к (непринятой в 17м стандарте) возможности вызывать метод класса как method(object, arg), добавить аналог this для аргументов из C#, когда один из аргументов для функции можно написать через точку слева, как будто это вызов метода: arg1.func(arg2).
Этим занимаются Страуструп и Габриель Дос Райс, но предложению предстоит ещё несколько итераций доработки. Вот последний proposal на эту тему http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0416r0.pdf
Antibiotic
14.07.2016 07:46+2По поводу if (std::lock_guard lock(m); !container.empty())
Не могу в данный момент проверить, но не должно ли if (std::lock_guard lock(m), !container.empty()) делать тоже самое уже сейчас?JIghtuse
14.07.2016 08:17+3Делает, но вы не можете одновременно объявить переменную и проверить её на произвольное условие.
Здесь подобное обсуждалось.abikineev
14.07.2016 21:14+1В Вашем примере мьютекс захватывается и сразу освобождается перед сиквенс поинтом — оператором,
В случае if (std::lock_guard lock(m); !container.empty()) lock живет в течение всего if стейментаDistortNeo
15.07.2016 00:02Вот только такой if коварен. Если в for заменить точку с запятой на запятую, то будет ошибка, а если в новом if — будет оператор запятая.
khim
15.07.2016 02:46Не будет. Оператор запятая тут недопустим. Вы должны либо описать ровно одну переменную (C++14), либо поставить-таки точку с запятой.
ittakir
14.07.2016 08:16+7Шел 2016 год, а я так и не мог написать в программе std::optional; (ага, я в курсе что через пару лет заработает).
Зато постоянно добавляют кучу адовой эзотерики. Если раньше я мог сказать, что знаю С++, то теперь уже точно нет.
Интересно, помогает ли все это это языку? Если 10 лет назад на нем писали почти всё, то теперь он скатывается в нишевые направления — 3D движки, OpenCV, микроконтроллеры и немного десктопного Qt (сужу по заказам на UpWork).
Лично для меня, как стандартный C++/STL был непригодным к использованию 10 лет назад, так он и остается до сих пор (Строки, работа с файлами, сеть, потоки и т.д.). Даже долбаное кросплатформенное приложение с нативным GUI интерфейсом невозможно создать (когда там графический интерфейс появился, в начале 90х?). Утешает только Qt.antoshkka
14.07.2016 08:23+8Вы забыли про браузеры, современные игры, высоконагруженые сервера, оффисные пакеты, программы для 3d моделирования, потрошки Андроида.
ittakir
14.07.2016 09:19-8Браузеры, офисные пакеты, 3d моделирование и прочие десктоп-приложения вполне успешно пишутся на C# и Java.
Игры тоже.
Сервера гораздо проще не высоконагружать, а горизонтально отмасштабировать, купив железа. А написать на языке, в котором как минимум строки Юникод поддерживают, не говоря уже о встроенной поддержке БД и т.д.Tujh
14.07.2016 09:26+8Просто в рамках самообразования, можно посмотреть на браузер целиком написанный на С# или Java? Ну и сервер, выдерживающий проблему C10k?
ittakir
14.07.2016 09:35-10Давайте говорить про доступные на рынке работы. Количество браузеров к общему количеству написанного ПО ничтожно мало. Я не знаю, на чем написаны конкретные браузеры, но не вижу большой проблемы написать их на C# или Java.
Сервер с C10k есть прямо в статье https://en.wikipedia.org/wiki/C10k_problem это некий MigratoryData на Java на 10–12млн подключений.Tujh
14.07.2016 09:53+7Как-то странно у нас диалог пошёл. Сперва Вы пишите:
Браузеры… вполне успешно пишутся на C# и Java.
затем:
Я не знаю, на чем написаны конкретные браузеры, но не вижу большой проблемы написать их на C# или Java.
Так пишутся или «не знаю, но обсуждаю»?
Про С10к Я могу привести гораздо более развёрнутую статью с французской вики где про MigratoryData даже и не сказано. Что такие сервера есть, я уверен, есть вполне успешный сервер на питоне умеющий в С10k, но в основе они всё равно обращаются к чисто системным вызовам epoll/kqueue/iocp и так далее, но это не часть «стандартной» Java, почему я и попросил ссылку. Да и MigratoryData целиком коммерческий, может поэтому по нему очень мало информации в интернетах, в отличии от остальных (с десяток, не меньше, и бесплатных).
webkumo
14.07.2016 12:41Немного не в курсе — в JavaFX браузер нативненький или java-based? И ещё куча всяких embedded-браузеров. Да это не браузер общего назначения… но в браузерах общего назначения итак не протолкнуться.
Насчёт серверов общего назначения — на Java, скорее всего, нет. Есть различные контейнеры приложений (tomcat/jetty/...), сколько они держат подключений на практике — не знаю, не интересовался. С учётом того, что основная их задача — не отдача статики, а отработка запроса в приложениии — проблемы раньше упрутся в особенности приложения.
Ну а сервер общего назначения, выдерживающий c10k я знаю один, но он не на плюсах а на голом C написан (nginx). На плюсах есть аналог?monah_tuk
14.07.2016 14:03Сервера разные, вот вам пара на C++: Nimble и EvoStream, но если посмотреть внутрянку (по импортируемым симвалом), то там беркли сокеты и poll/kqueue.
sborisov
14.07.2016 14:53boost.asio дёргает внутри себя
(epoll на Linux 2.6, kqueue на FreeBSD/MacOSX, Overlapped IO на MS Windows)monah_tuk
15.07.2016 04:46И что? Asio там не используется, Nimble — косвенное заключение да и за чаем разработчика спрашивал, а исходники EvoStreamer до того как он стал закрытым доступны: crtmpsserver, там тоже нет Asio. Правда, думаю, сейчас Evo от своего прародителя далеко ушёл.
А по своему опыту: нужно было написать молотилку запросов HTTP (много мелких) Asio раскачать сильно не получилось, упирался в ~250к RPS, тогда как на libev (у него встроенный C++ интерфейс есть) запросто получилось около 600к RPS. Правда это на 1k подключений, на 10к снизилось где-то в полтора-два раза, на обоих.
Минус решений отличных от libuv и asio: для Windows, по сути, приходится писать свой код.
Ну и ещё, Asio и libuv сильно обмазывают epoll снаружи, что бы его использование походило по интерфейсу на IOCP
sborisov
14.07.2016 14:50вот тут статья от mail.ru про c10k и boost.asio
https://habrahabr.ru/company/mailru/blog/191756/
billyevans
15.07.2016 01:51Есть не совсем аналог. Но несколько лет назад он раза в 3 работал быстрее, чем nginx.
https://github.com/mamchits/phantom
На нем еще построена тулза для нагрузочного тестирования Яндекс.танк.
sborisov
14.07.2016 14:30+2Нет, нет, нет… заберите всё это себе… Никаких браузеров на Java и C#, кстати где вы их взяли? :)
webkumo
14.07.2016 18:09Хм… а чем вам не нравятся браузеры Java? Небольшие эмбеды в различные java-приложения… удобно же? Согласен, что они добавляют свои нюансы (в них не втюхивается столько сил, сколько в вебкит и файрфокс, поэтому они несколько отстают от стандарта), но их и используют только в определённых задачах (различные плюшки в тех самых java-интерфейсах; различные генераторы, например pdf).
Вообще я был бы рад, если бы они развивались, но нужда сообщества в них достаточно мала, также как и интерес крупных компаний :(
sborisov
14.07.2016 14:32+2Помню когда начинались «одноклассники», был С#, потом Java и какие были тех. проблемы, когда пошёл взрывной ростаудитории и оказалось, что стоимость железа будет просто огромна, да и ЦОД таких практически не было.
RomanArzumanyan
14.07.2016 19:12-2Про потрошки Андроида — назовите меня фанатом, но таки на С. Ибо есть высокоуровневая Java, и лично я не вижу смысла прятать под капотом ещё один высокоуровневый язык программирования. Выглядит как костыль.
ilmirus
14.07.2016 19:54+1Первая же ссылка из геррита андроида ведет на плюсы:
https://android-review.googlesource.com/#/c/247370/5/compiler/optimizing/code_generator.cc
В андроиде на самом деле почти весь user-space на плюсах.khim
14.07.2016 23:25+6Лучше сюда посмотрите. Это не какой-то там комилеро-генератор, а, на минуточку, начальный загрузчик. То есть то, что стартует в самом-самом начале, до любой программы.
На C в Android'е — только некоторое количество legacy-кода (взятого часто из разных BSD).
Вообще весь Android — это немного «театр абсурда»: почти весь Android — написан на C++, большинство популярных программ — тоже. Но взаимодействовать им приходится через Java'у обходя кучу костылей, нужных для этого монстра. Вот что бывает когда PR ставят выше технической целесообразности… с другой стороны если бы не PR, то Android фиг бы «взлетел»: его ранние версии, по большому счёту, были весьма убоги, если бы не волшебное слово «Гугл», то не факт что его бы кто-либо сейчас ещё использовал…
RomanArzumanyan
15.07.2016 12:20С моей точки зрения — потрошки Андроида на плюсах — это не есть хорошо. Вот это я и хотел сказать в своём комменте. То, что они по факту написаны на плюсах — я знаю.
TreyLav
14.07.2016 08:50+7Даже долбаное кросплатформенное приложение с нативным GUI интерфейсом невозможно создать
Единственный язык, который я вспомнил с ходу, со своей собственной реализацией кроссплатформенного GUI — это Java. И, знаете, как-то оно меня тоже не радует (как пользователя). Уж лучше Qt или другой «универсальный» тулкит.
sborisov
14.07.2016 14:28+1Раньше главные претензии были, что мало функций, маленькая стандартная библиотека, нужно больше всего, и теперь когда это пытаются дать, начали раздаваться крики «горшочек не вари». :)
khim
14.07.2016 14:59И оба мнения правильны, что характерно. Библиотеки нужно оформлять как отдельные подстандарты — и реализации смогут пречислять какие из них поддерживаются. Да и с языком тоже самое: Fixed point существует как отдельная сущность от всего остального — что не мешает его использовать тем, кто он нужен.
TargetSan
14.07.2016 15:00+4Это реально "горшочек". Одно только внедрение аналитической геометрии в стандарт. Хотя ей самое место в отдельном пакете. Т.е. в стандарт тащат много, но не всегда то, что реально нужно.
0xd34df00d
14.07.2016 22:34Почему же? Мне вот аналитическая геометрия реально нужна, например.
TargetSan
14.07.2016 23:24+7А я не говорю, что аналитическая геометрия это плохо. Я говорю, что ей место в отдельной либе. Пусть она поддерживается комитетом, не вопрос. Но отдельно. Иначе такими темпами в стандарт захотят 3Д-движок, физический движок, библиотеку акторов и что-нибудь ещё. И тогда стандарт будет не 1600 а 16000 страниц.
antoshkka
15.07.2016 08:46Другими словами, вы не хотите один документ с 16000 страниц, а хотетие 10 документов по 1600.
Мне кажется это вопрос оформления/вкуса/дизайна/скрипта-сборки-документа. О чем спор то? :)ozkriff
15.07.2016 09:04+3Как минимум, если что-то попадает в стандарт самого языка — это оттуда потом уже хрен выкинешь или нормально поменяешь — надо будет таскать с собой вечно в обратно-совместимом виде. А чем менее фундаментальна библиотека, тем больше шансов что она быстро устареет и станет бесполезной.
dpantele
15.07.2016 23:17Да не надо же вечно таскать, вполне себе выкидывают std::binary_function, например. Реализация какое-то время бдует содержаться, в этом проблема, да.
EvgK
19.11.2016 10:26Я специально узнавал у двух разных профессоров про данную конкретную ситуацию, и оба подтвердили этот факт. После прохождения даун конвертеров, новые фотоны не когерентны (потому, что они образуются в разных случайных точках внутри кристалла даун конвертера, а не в одной конкретной точке) и не образуют интеренференционной картины.
Videoman
14.07.2016 17:27+1На мой взгляд, тут дело не в том, чтобы остановить поток лавы. Все эти разношерстные фичи толкаются разными разработчиками библиотек и они реально им нужны для макро и микро оптимизаций. Они также делают много обобщений и постоянно сталкиваются с дублированием кода и им нужно все это оптимизировать. У этих людей весь С++ загружен в «кеш». Т.е. они полностью погружены в потроха С++ и глядя на код видят результат компиляции. Я сам, когда долго пишу низкоуровневую библиотеку, перехожу в это состояние. Но когда начинаешь решать реальную бизнес задачу, «наверху», использую свои наработки, то все эти детали быстро улетучиваются. И что делать прикладным программистам в этом случае??? Тут нужно вырабатывать автоматизм, а с таким зоопарком это очень трудно сделать. Приведу пример:
Был сеттер:
void SomeClass::SetName(const std::string& first, const std::string& last);
Теперь, в новом стандарте, есть move сематика. Теперь в лоб, получается, что для того, чтобы работали идеи авторов стандарта нужно писать что-то типа того:
void SomeClass::SetName(const std::string& first, const std::string& last);
void SomeClass::SetName(std::string&& first, const std::string& last);
void SomeClass::SetName(const std::string& first, std::string&& last);
void SomeClass::SetName(std::string&& first, std::string&& last);
т.е., на лицо, логарифмический рост количества перегрузок функций.
Я понимаю, что нужно теперь писать вот так:
void SomeClass::SetName(std::string first, std::string last);
и компилятор сам разберется, но что бы это понять и убедиться что это работает, нужно столько сил и времени. В итоге имеем такие затыки через каждые 20 строчек.Antervis
14.07.2016 19:46+1> но что бы это понять и убедиться что это работает, нужно столько сил и времени
Просто: потому что copy elision 1. и так реализован в основных компиляторах 2. является требованием с++17.
> В итоге имеем такие затыки через каждые 20 строчек
Вот когда замерите, увидите что у вас программа 30+% времени перегоняет пустое в порожнее, туда-обратно копирует и жрет оперативку как хром, тогда у вас будет замечательный инструментарий в виде lvalue reference/rvalue/copy elision для оптимизации копирования и использования памяти. А заниматься микрооптимизациями, да еще и без четкого понимания как это всё работает — вредить себе и проекту.Videoman
15.07.2016 10:49По сути, все о чем мы тут рассуждаем и есть сплошные микрооптимизации. Это все базовые кирпичики размазанные тонким слоем и никого HotSpot-а в 30% в базывых примитивах в адекватном коде на С++ вы не увидите. С точки зрения высокоуровнего программиста, можно писать хоть на javascript. Ему С++ не нужен.
Antervis
15.07.2016 10:58я немного про другое. Писать несколько лишних перегрузок функций, каждая из которых сэкономит всего по несколько процессорных тактов, займет больше времени, чем эти несколько тактов сэкономят за всё время жизни приложения.
Videoman
15.07.2016 11:09Я согласен. Но, если не важны такты, то и С++03 достаточно, и все эти микрооптимизации интересны только комитету и чего мы тогда тут страдаем :)
Antervis
15.07.2016 12:49потому что из 100 случаев в одном эти микрооптимизации таки принесут пользу
Videoman
15.07.2016 14:00Это не так. Конструктор перемещения теперь присутствует, в том или ином виде, почти в любом классе и везде теперь придется учитывать возможности нового стандарта. Иначе все эти усложнения просто бессмысленны.
Antervis
15.07.2016 16:37Нет. Во-первых, есть много сценариев, при которых компилятор вправе сделать placement конструктор вместо перемещения. Во-вторых, многие операции перемещения в stl легальны только если соответствующие конструкторы/операторы присваивания объявлены noexcept. В-третьих, как я уже писал, перемещающие конструкторы/операторы присваивания неявно создаются только для классов, у которых не переопределены копирующий конструктор/оператор присваивания/деструктор. В четвертых, везде, где у класса не определена соответствующая мув-семантика, компилятор использует копирующие аналоги.
Сценариев, где неявно используется семантика сдвига вместо копирования, не так уж и много и наткнуться на ошибки при их использовании попросту невозможно: это требует ручного некорректного определения мув-семантики в используемых классах.Videoman
15.07.2016 16:42Спасибо, я в курсе. Я не про корректность, я про то, что когда пишешь свои библиотеки и хочешь использовать преимущества современно С++, теперь, все это придется учитывать.
GraD_Kh
16.07.2016 11:30+1Все-таки перегрузки с const std::string&& бессмысленны. Достаточно иметь 2 перегрузки — const std::string& и std::string&&. Однако, если речь идет о сеттере, внутри которого значение захватывается по значению, то при наличии rvalue-ссылок можно передавать просто по значению:
void SomeClass::SetName(std::string first, std::string last) { _first = std::move(first); _last = std::move(last); }
Поскольку move очень дешев, то это будет практически оптимально для всех случаев:
foo->setName(first, last); foo->setName(std::move(first), std::move(last)); foo->setName("Ivan", "Pupkin");
DistortNeo
16.07.2016 17:10Так предыдущий комментатор именно это и написал. Если есть 1 параметр, то будет 2 перегрузки, если 2, то 4, если 3, то 8 (дял всех возможных комбинаций) и так далее.
GraD_Kh
17.07.2016 12:15Мой комментарий был о том, что практически всегда можно оставить один из вариантов, в зависимости от использования данных внутри функции — либо передавать по значению, если внутри метода сохраняются данные, либо передавать по константной ссылке. И не нужно большого числа перегрузок.
myrov_vlad
14.07.2016 15:03А зачем использовать один язык для _всего_?
По-моему у каждого языка есть свои плюсы и минусы и, следовательно, он подбирается исходя из требований решаемой задачи.
c++ отлично подходит, если нужно написать какую-нибудь высокопроизводительную и сложую систему, но зачем его использовать там, где есть более удобные инструменты, пусть даже это и отразится в меньшей производительности и большем потреблении памяти, когда это не является критичным.
JustTry
14.07.2016 08:16-2Сколько читаю стандарты, начиная с С++11, и нигде не вижу и намёка на встроенные библиотеки для графики. Неужели их никогда не введут?
Обычно в языках программирования стараются снизить порог вхождения, сделать язык более привлекательным для людей. А в С++ читаешь обновления и думаешь: «А дойду ли я когда-то до этого?». Я пока новичок и решаю более простые задачи, для которых мне совсем не нужно большинство вещей из С++11/14/17. Но вот та же графика — почему ее нет? Приходится качать и подключать Qt. Аналогично с базами данных. И что забавно — в С++ всегда какие-то проблемы с подключением других библиотек. Как-будто это специально создано для мучения:)
Хотя, самом деле, язык С++ мне очень нравится. Это мой первый язык и мне приятно с ним работать. Просто хочется, чтоб наконец-то его попытались упростить для нужд простых смертных.antoshkka
14.07.2016 08:29С радостью помогу с написанием предложений по стандартизации подключений к базе данных. Но тут нужен достойный прототип.
Графику наврядли получится стандартизировать. Слишком большой выбор: есть Qt, WxWidgets, Gtk, EFL и многие другие.JIghtuse
14.07.2016 08:41Был proposal с добавлением Cairo для 2D-графики. Он довольно низкоуровневый (GTK, Firefox его используют под капотом) и достаточно кросплатформенный. Но не знаю, как он там продвигается.
antoshkka
14.07.2016 09:10Cairo-подобный proposal сейчас выглядит вот так: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0267r1.pdf
Предложение очень активно обсуждалось в Оулу, вносилось множество правок. Есть шанс через пару лет увидеть в std::experimental::
Dark_Daiver
14.07.2016 09:01+1Зачем языку, программы на котором нередко исполняются в средах где не требуется GUI встроенные средства для графики?
В отличие от многих новых языков перед С++ проблема привлечения новых пользователей стоит не так остро, поэтому особо снижать порог вхождения не требуется.
Для «простых смертных» есть куча других языков, которые сознательно стараются упростить процесс разработки (порой, правда, ценой производительности/гибкости/узкой специализации)encyclopedist
14.07.2016 15:46В первую очередь для обучения. Детей, школьников. А то они спрашивают "а как мне нарисовать черепашку?" и получив ответ с отсылкой к каким-то непонятным библиотекам, выбирают другой язык.
Dark_Daiver
14.07.2016 16:26+2С++ не очень подходит для обучения, ИМХО, даже совсем.
Ну и в таком случае, надо отсылать к понятным библиотекам.
JustTry
14.07.2016 17:21Да, для большинства задач, обычно решаемых с помощью С++, можно обойтись без GUI. Но с другой стороны, иногда он все-таки нужен:
1. Как уже ответили, для обучения. Приложения без графики новичкам писать не так интересно.
2. Для своих приложений, которые используешь для получения опыта в использовании языка. Просто решаешь свои прикладные задачи, пишешь для этого простые программы с интерфейсом (Да, есть много других языков, где можно с легкостью создать GUI, но ведь цель — изучить С++).
3. Да и просто для создания быстрых программ с интерфейсом. С++ ведь достаточно хорош в скорости, так почему б его не использовать для этого?Dark_Daiver
14.07.2016 18:45+1Ну я же не предлагаю совсем отказаться от GUI в С++, я говорю, что в стандартной библиотеке (которая должна создаваться из расчета, что ее API не будет меняться десятилетиями, и что она будет реализованна для множества платформ) GUI явно излишен.
Для графического интерфейса, в мире С++ есть решения. Тот же Qt.
Если нужен быстрый старт, то берем Qt Creator и вперед. Подозреваю что подключить Qt в других IDE тоже не особо сложно.
sborisov
14.07.2016 15:01я раньше обычно для работы с БД использовал QtSQL модуль, раз уж приложение с графикой, сейчас просто подключаю libpq — и ну его нафиг — тот QtSQL. Т.к. мне доступны все возможности БД, библиотека отлично документирована, много примеров. Т.ч. не так всё страшно.
Можно сделаьт класс обёртку, с методами — а под капотом делать бекенды которые могут с любой БД работать, хоть mongo, хоть oracle
iperov
14.07.2016 09:02+1Объясните мне,
— что теперь каждые 3 года, когда вводят новые фишки в C++, миллионы строк кода готовых оттестированных кодобаз (библиотеки, игровые движки) должны кинуться обновлять свои сорсы под эти фишки, рискнув стабильностью своих продуктов?
так и представляю: миллионы коммитов в древних не тронутых файлах, где меняется самое простое допустим xx::yy::zz::iterator на auto.
— или эти новые фишки задумываются под новые проекты?
но подождите, какие новые проекты, если уже всё изобретено. Пропагандируется же: не изобретай велосипед, бери и пользуйся готовым.
— или предполагается совмещение стилей С++03 с новыми стандартами в одном продукте?
т.е. листаешь ты сорсы, в одном файле xx::yy::zz::iterator, в другом — auto. Очень всё лаконично же и в одном стиле, не код, а загляденье.DistortNeo
14.07.2016 10:13+1Принципиальных изменений в C++ не происходит. Происходит только медленное разрастание STL и добавление синтаксического сахара для увеличения простоты и уменьшения объёма кода.
Единственное фундаментальное изменение — это move semantics.
А так да, можно писать в стиле C++03.iperov
14.07.2016 10:59Простота с STL не увеличивается, когда мультишаблонный тип занимает полэкрана в объявлении переменной.
boost'у это можно простить, потому что они выжали максимум средствами языка, но бюро стандарта сделали эти костыли стандартом.
Предлагают заменять на auto — тогда каждый раз нужно лезть в подсказку и узнавать что это за тип, теряется концепция быстрого чтения кода.
Move semantics прочитал здесь. «гениально», стреляем себе в ногу дальше. Теперь каждый раз нужно лезть в класс, и смотреть, а нет ли у него move semantics внутри, чтобы знать, что этот класс нельзя использовать после копирования foo&.antoshkka
14.07.2016 11:05+1нет ли у него move semantics внутри, чтобы знать, что этот класс нельзя использовать после копирования foo&
Я вас не понял, распишите пожалуйста проблему подробнее.
DistortNeo
14.07.2016 11:19+1Касаемо языковых фишек: простота увеличивается за счёт использования лямбд (больше не надо писать классы-функторы), range-based for (чуть меньше кода), constexpr (вместо шаблонов и метапрограммирования). Понятность — за счёт явного указание override и final для виртуальных функций, nullptr вместо константы NULL, отказа от enum в пользу enum class.
auto считаю злом.
Замечание про move semantics не понял. Любой старый класс будет неявно поддерживать перемещение, если не указано обратное. И всё будет работать корректно, но до тех пор, пока конструкторы копирования и перемещения будут выполнять только то, что они должны, без побочных действий.Antervis
14.07.2016 19:54Класс будет неявно создавать перемещающий конструктор/оператор присвоения только в случае, если у класса не переопределены копирующий конструктор/оператор присвоения или деструктор. А их как правило переопределяют для нетривиальных классов.
DistortNeo
15.07.2016 18:09Да, действительно. Я просто заметил, что практически перестал использовать явные конструкторы копирования и перемещения.
0xd34df00d
14.07.2016 22:47-1Почему auto зло?
DistortNeo
15.07.2016 00:03-3Усложняет читаемость кода.
0xd34df00d
15.07.2016 00:12+1При должном именовании методов и переменных — ничуть.
DistortNeo
16.07.2016 21:25Использование auto — как использование паттернов. Есть люди, которые считают нужным притягивать за уши паттеры там, где они не нужны.
auto хорош для локальных переменных, возможно, некоторых приватных методов. Но нельзя допускать, чтобы публичный метод возвращал auto, кроме редких случаев, когда используются шаблоны и вывод громоздок или крайне затруднителен.0xd34df00d
18.07.2016 04:36+2Для публичных методов — безусловно, согласен. Более того, когда я пишу на хаскеле, которому вообще аннотации типов почти всегда не нужны, я их, тем не менее, для top-level-функций указываю.
semenyakinVS
15.07.2016 18:59Не стоило это делать. Не повторяйте мой опыт. auto — это просто инструмент, который нужно использовать к месту — и тогда он бывает удобен.
TargetSan
14.07.2016 11:22+1Вообще-то в rvalue reference превращаются только rvalue. А 'foo& bar' — lvalue. Здесь как раз косяков нет. Хотите переместить именованное значение — std::move. Но если после него вы используете старую переменную — ССЗБ.
iperov
14.07.2016 12:46-1Если у меня уже отлаженная парадигма классов в сервере, зачем мне добавлять новые классы с непонятным извне поведением, которое приведет к ССЗБ?
Весь этот огород с move semantics, когда однозначно не понятно, в каких случаях класс копируется, а в каких перемещаются кишки, и тогда старая переменная не валидна для использования, потому что внутренний хендл переместили. И что тогда в каждую функцию File добавлять проверку на валидность хендла? Адская избыточность. Зачем эти усложнения?
В той хабра статье уповают только на использование динамической памяти там, где она мол не нужна.
Какие сейчас проблемы с памятью? У меня на сервере свой MM, предвыделенные чанки (расширяются автоматически) от 8 байт до 32кбайт с шагом по экспоненте, в неблокирующем ring buffer'e.
Выделить 8-32кбайт на хендл затрат проц времени для ММ около нуля, зато получаем более управляемый код. Это я имею ввиду использовать хендл в обвязке умного указателя.
Сейчас тенденция везде использовать умные указатели для управляемости и читабельности, и я не понимаю смысла ввода move semantics в виду этой тенденции, чтобы ворочать сырыми указателями в не обвязанных классах, создавая неопределенное поведение классу для пользователя извне, не знакомому с внутренней работой класса.mayorovp
14.07.2016 13:00+2Да почему непонятно-то? Как раз наоборот, все предельно просто.
"Кишки перемещаются" ровно в двух случаях:
- когда старое значение более не доступно;
- когда программист явно написал
std::move
Если вы не будете использовать
std::move
— вы никогда не натолкнетесь на невалидные переменные.
Сейчас тенденция везде использовать умные указатели для управляемости и читабельности, и я не понимаю смысла ввода move semantics в виду этой тенденции
Все просто: move semantics придумали чтобы сделать нормальные умные указатели. К примеру,
uniqie_ptr
без семантики перемещения нельзя было бы использовать в STL-контейнерах.TargetSan
14.07.2016 13:22+3Все просто: move semantics придумали чтобы сделать нормальные умные указатели. К примеру, uniqie_ptr без семантики перемещения нельзя было бы использовать в STL-контейнерах.
Не только для них. Чтобы не иметь копирований на ровном месте, где их быть не должно.
Videoman
14.07.2016 17:55-3Ничего не просто. При move, копирование все-равно происходит. Это не панацея, а всего лишь еще один уровень оптимизации. Если у вас класс имеет 100 полей move придется их все копировать. Т.е. RVO и Copy-Elision предпочтительней, а const& все еще нужен. Этот экспоненциальный рост количества перегрузок, при проектировании интерфейсов, не добавляет простоты.
Antervis
14.07.2016 20:04-1если при муве происходит копирование, значит вы что-то делаете не так
Videoman
15.07.2016 10:54Да конечно… :). Например в std::string, обычно, есть оптимизация, где маленькие строки, до 16 байт, хранятся в статическом массиве в самом std::string. Как, по вашему, должен быть реализован конструктор перемещения в таком случае?
Tujh
15.07.2016 11:58-1Вот только это «обычно» относится к одной-двум версиям библиотек и с появлением С++11 я не уверен, что этот «финт ушами» сохранился. В С++03 были и реализации, которые строку хранили по частям, разбивая на блоки фиксированного размера, в С++11 появилось требование о непрерывности блока памяти, хранящего данные строки.
Videoman
15.07.2016 12:06+1Я смотрел. Он сохранился. Более того, это теперь почти единственная оптимизация для строк которая есть на практике, т.к. требования к строке в С++11 таковы, что теперь даже COW невозможен и все реализации библиотек переделываются чтобы учитывать новые требования.
antoshkka
15.07.2016 12:16В C++11 этот финт ушами только и разрешили делать. Начиная с C++11 все реализации строки с COW или хранящие строку по частям, или не держащие на конце '\0' — не являются валидными имплементациями.
И слава богам! Замена COW строки на строку со small-buffer optimization в двух абсолютно различных проектах давала прирост производительности более 10% (мой личный опыт). Знакомые из других проектов подтверждают прирост в производительности.
Для не верящих есть целая статья http://www.gotw.ca/publications/optimizations.htmTujh
15.07.2016 14:20Получается, что в случае «длинных» строк, мы получаем overhead по памяти, за счёт не используемого буфера в те самые 16 байт? Или его начинают использовать в других целях (указатель на память в куче и т.п.)?
TargetSan
15.07.2016 10:33Если вы таскаете по стеку структуру на 100500 полей, вы делаете что-то не так. А мув служит для почти бесплатного перемещения контейнеров, у которых маленьая часть на стеке и большая в куче. Простой пример — вектор на пару сотен килобайт. Без мува, при отдаче владения в функцию, у вас будет копирование. Толстое. С выделением ещё одного куска на пару сотен килобайт и копированием всего содержимого. С мувом — старый вектор отдаст новому владение буфером. И будет "копирование" 1 указателя. С занулением его на старом месте.
Videoman
15.07.2016 11:02-1мув служит для почти бесплатного перемещения контейнеров, у которых маленьая часть на стеке и большая в куче
Бесплатного для того кто использует, а для того кто пишет эти классы :)?TargetSan
15.07.2016 11:14+3Что я могу сказать. Не нравится — не используйте. Как указывалось много раз выше, перемещение включается в конкретных, чётко описанных ситуациях.
Если же вопрос в "плохо спроектировано" — в каком-то смысле да. Основная проблема — старое местоположение остаётся доступным, даже после мува. Если бы это блокировалось компилятором, то и мув-конструкторов, пожалуй, не понадобилось бы. Но сделать старое местоположение недоступным в рамках семантики С++ нельзя — любое значение является прежде всего пачкой байт, которые можно копировать во все концы. А конструкторы перемещения, операторы присваивания и т.п. представляют собой своего рода хуки на эти действия.
Videoman
15.07.2016 11:26Я не говорил что мне не нравится С++, и в силу специфики работы мне нужен С++. Я просто хочу сказать о том, что кроме расширения стандарта, нужно заботится о том, что бы люди могли его правильно использовать. А вот тут информации и книг не достаточно пока, на мой взгляд.
Tujh
15.07.2016 11:59+1Майерс доступен уже даже на русском с полгода минимум. Как обычно — всё пережёвано и готово к употреблению.
encyclopedist
15.07.2016 17:07+1Почти бесплатно.
Современная концепция состоит в том, что управление ресурсами должно осуществляться в специальных листовых классах. Это могут быть умные указатели или самописные классы. В этих классах да, придётся самому написать все 5 спец-методов (см "Rule of Five"). Но таких классов должно быть немного (по одному на каждый вид ресурса, причём для самых распространённых ресурсов "память" и "открытый файл" такие менежджеры уже реслизованы в стандартной библиотеке).
Остальные же классы (которых большинство) вообще не должны заниматься управлением ресурсами и не должны реализовывать спец функции сами. Они просто должны включать в себя классы-менеджеры ресурсов, и компилятор всё остальное сделает сам.
Эта концепция известна под именем "Rule of Zero".
Videoman
15.07.2016 17:12Ох уж эти теоретики… хорошо, запомним. Будем стараться осваивать все эти современные концепции. Главное, чтобы они не менялись быстрее, чем программисты их успевали изучать.
Dudraug
14.07.2016 14:40+3Теперь каждый раз нужно лезть в класс, и смотреть, а нет ли у него move semantics внутри, чтобы знать, что этот класс нельзя использовать после копирования foo&.
Мне кажется вы не поняли move сементику. Проблемы никакой нет. Прочитайте еще раз до полного просветления.
johny
14.07.2016 10:29Зачем обновлять сорсы под фишки? можно просто сказать, что поддерживается такая-то версия языка.
Не нужно кидаться и обеспечивать поддержку всего-всего на свете.
Как-то в сфере веба довольно часто всё обновляется, но живет же как-то эта сфера.
Tujh
14.07.2016 09:02+2std::string::data(), возвращающий неконстантый char* (УРА!);
Может я не сталкивался просто, а что не так было с константностью этого метода? Вполне логично, что получая «сырые» потроха строки их нельзя менять, как мне кажется.Antervis
14.07.2016 11:21проблема в том, что некоторые неблагородные API (и майкрософт грешит этим чаще остальных) часто просят, например, wchar_t* вместо const wchar_t* даже когда не меняют строку.
Gummilion
14.07.2016 15:51Для таких неблагородных есть const_cast, зачем еще новый метод добавлять?
0xd34df00d
14.07.2016 22:49const_cast пахнет. Очень.
khim
14.07.2016 23:35И хорошо что пахнет: сразу видно где у вас кривизна в API. А так — вы её просто будете вытаскивать дальше по стеку. И у вас тоже появятся функции, принимающие
std::string&
вместоconst std::string&
, но при этом строку не меняющие. Кому от этого лучше станет?
Мне вот интересно в какой версии C++ конструктор string'а станет наконец-тоconstexpr
… В 20й? Или в 50й?
Как писал Джоел: Интересно, что историю развития C++ можно описать как историю затыкания дырок в абстракции строк. Уж не знаю, отчего бы не добавить к языку элементарный класс строчек.
Пока что наблюдение продолжает иметь силу :-)0xd34df00d
15.07.2016 00:15Лучше станет мне: глаза вытекать не будут. API какого-нибудь там Windows я всё равно поменять не в состоянии, так чего мне нервничать лишний раз?
А функции не обязаны приниматьstd::string&
вместоconst std::string&
, достаточно передаватьstd::string
, по значению на самом нижнем, оборачивающем уровне.DistortNeo
15.07.2016 18:05Если передавать std::string, то будет лишнее копирование при невозможности перемещения при передаче параметра в функцию.
0xd34df00d
16.07.2016 20:21Да, но это, возможно, и не важно, особенно если вы дёргаете функцию для I/O или, того хуже, сети.
DistortNeo
16.07.2016 21:21Да, в случае работы с сетью частая ошибка: уничтожить буфер до того, как он будет фактически отправлен. Тут действительно лучше скопировать — надёжнее будет.
0xd34df00d
20.07.2016 01:16Конкретно тут не соглашусь, разницы нет никакой (если не учитывать многопоточность) :)
Если вам в функцию передали константную ссылку, которая жива на момент начала исполнения функции, то она будет жива до её окончания. Точно так же и с локальной копией: локальная копия будет жива до окончания исполнения функции.
Antervis
15.07.2016 06:02+1> Мне вот интересно в какой версии C++ конструктор string'а станет наконец-то constexpr…
Если вы знаете, как сделать constexpr-конструктор для класса, владеющего памятью на куче, срочно оформляйте proposal! Это же столько дряни в компайл-тайм перетечет!bull1251
19.11.2016 03:03-1На самом деле это только начало эксперимента, позволяющий понять суть происходящего. Поэтому в тексте я указал:
В качестве примера рассмотрим эксперимент с отложенным выбором, который был предложен Скалли и Дрюлем:
В дальнейшем установка усложняется. Причем дальнейшее действия будут во многом отличаться от предложенного эксперимента Скалли и Дрюля. Я не стал описывать всех деталей предполагаемого мной эксперимента, оставив для читателя возможность самостоятельно решить эту задачу. А чтобы получить интерференционную картину, в последующем необходимо будет стереть информацию о том, через какой путь прошли фотоны. Например, направив холостые фотоны обеих направлений в один даун-конвертор, дающий на выходе «50 на 50» вероятности того, через какой путь прошли фотоны.
Я читал ваши комментарии, и в особенности хотелось бы отметить данный текст:
Если фотон попал в D1 или D2 — информация о пути «стерта». Если мы удалим маркеры со всех фотонов, они все попадут в D1 или D2. Но важный момент в том, что мы получим ДВЕ интерференционные картины (для фотонов из D1 и D2 соответственно), причем пики одной совпадают с впадинами другой. Соответственно эти две картины в сумме дают обычное хаотическое распределение (которое мы и видим на экране).
Дело в том, что ДВЕ интерференционные картины при наложении дают хаотическую картину, только когда используются два лазерных луча разного спектра (красного и синего). Предположение верное, но только в рамках указанного вами эксперимента. А когда используется один лазер, такого смещения не происходит.antoshkka
15.07.2016 08:54Это будет std::string_view а не std::string.
std::string_view имеет в C++17 constexpr конструкторы и операторы, но часть операций зависит от CharTraits, где аналогичные операторы тоже должны быть помечены как constexpr.
Antervis
15.07.2016 09:27если говорить конкретно про std::string, то тогда надо будет добавлять флаг того, что строка ссылается на константную строку в памяти. Соответственно, при попытке модифицировать строку будет происходить отложенное выделение памяти, копирование, модификация и пр. Т.е. сделать constexpr string(const char *) таким образом в принципе можно, вот только string+string или другие тривиальные операции уже constexpr не сделаешь. Помимо этого все операции будут проверять флаг и сама строка вырастет в размерах. Получается размен шила на мыло.
Вариант 2: сделать конвертируемую в std::string constexpr-оболочку поверх const char*. Тогда оболочка-то будет constexpr, а сам string будет создаваться из неё в рантайме. Т.е. тоже бесполезно.
crackedmind
15.07.2016 11:02+1Был пропозал отдельный на fixed_string http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0259r0.pdf На сайте автора пропозала можно найти реализацию.
TargetSan
14.07.2016 09:13Кстати, из чистого интереса. std::vector, std::string научились конструироваться из сырого буфера и отдавать свой сырой буфер?
Tujh
14.07.2016 09:18отдавать — вроде давно научились std::vector::data/std::string::data, начиная с С++11 метод data должен возвращать указатель на корректную нуль-терминированную строку.
The pointer returned points to the internal array currently used by the string object to store the characters that conform its value.
Both string::data and string::c_str are synonyms and return the same value.
http://www.cplusplus.com/reference/string/string/data/
pravic
14.07.2016 10:05string_ref не владеет буфером, поэтому можно её использовать в таких случаях.
Или нужно что-то наподобие unique_ptr, который принимает готовый указатель и владеет им, пока явно не отдашь (release)? Но зачем?monah_tuk
14.07.2016 14:07Отдать владение в какое-то API. Бывает необходимо. Сейчас аллоцируешь сам, копируешь и уже новое отдаёшь.
AxisPod
14.07.2016 10:22Капец, когда забривали всякие вещи, потому что они дублируют функционал и при этом замена была действительно лаконичной, объемы кода заметно уменьшались, а тут нате вам корявый if с инициализацией. Если уж хотелось что-то подобное, лучше бы сделали аналог using из C#, было бы куда полезнее, чем этот костыль.
mayorovp
14.07.2016 13:07+2Ну, реализовать
using
-то теперь можно без проблем:
#define using(x) if(x; 1)
AxisPod
14.07.2016 13:09Но это макрос, не красиво. Просто непонятно, зачем делать частичный функционал, а потом если захочется для while подобный функционал. Оно самом по себе как-то не так.
mayorovp
14.07.2016 13:22+1Для
while
такое уже есть, называетсяfor
:)AxisPod
14.07.2016 13:24Ну а если я хочу именно while, чтобы подчеркнуть, что мне не нужен блок итерации внутри инструкции. По вашей логике и вместо if был уже for.
xaizek
14.07.2016 13:57А вот это действительно странно, объявление переменных в условии это общее свойство if/switch/while/for, а расширили синтаксис только для if и switch. Причём добавлять в for может не захотели из-за изменения его структуры, а вот чего while пропустили непонятно.
Dionis_mgn
14.07.2016 14:24Для for оно работает уже уйму лет.
xaizek
14.07.2016 15:36Я не о i:
for (int i = 0; i < 10; ++i) ...
А о e:
for (int i = 0; error_code e = check_i(i); ++i) ...
Могли бы расширить (для единообразия, практической пользы тут не так уж и много) до:
for (int i = 0; error_code e = check_i(i); has_error(e); ++i) ...
(здесь e вычисляется и проверяется на каждой итерации)
laughman
14.07.2016 10:43«void get_vendor_from_id(std::string_view id) „
и тут же:
“Осторожно: string_view не гарантирует, что строчка, которая в нем хранится, оканчивается на символ '\0', так что не стоит использовать функции наподобие string_view::data() в местах, где необходимо передавать нуль-терминированные строчки.»
почему бы сразу не рассмотреть std::string get_vendor_from_id(std::string_view id)?antoshkka
14.07.2016 10:48+1Оператор вывода в поток перегружен для std::string_view. Это значит что строчки не оканчивающиеся на '\0' всё равно будут корректно выедены.
laughman
14.07.2016 11:32вопрос про вывод в std::string
antoshkka
14.07.2016 11:43std::string умеет конструироваться от std::string_view. std::string копирет строчку из std::string_view и добавляет '\0' в конец строки.
std::string_view id{"Vasya Pupkin:Super Stuff"}; std::string_view vasya_sb = id.substr(0, id.find_last_of(':')); std::string vasya{ vasya_sb }; assert(vasya == "Vasya Pupkin");
SergeySib
14.07.2016 11:26Почему же при такой любви Комитета к функциональщине, в стандартную библиотеку еще не внесли «джентльменский набор» из всевозможных apply, fold, map, zip, zipWith и т.д., создающих последовательности вызовов на этапе компиляции? Ведь все предпосылки уже есть, и часто приходится самостоятельно писать вещи для std::array или std::tuple с различной степенью кривизны. Планируется ли что-нибудь из этого в ближайшем будущем кроме уже внесённого apply для кортежей?
antoshkka
14.07.2016 11:35apply и make_from_tuple приняли в C++17
fold — в C++17 есть fold expression наподобие variable_pack +…
zip, map — нет изкоробочного, готов помочь с написанием proposal если вам интересно
MCoder
14.07.2016 11:26+1Согласен с высказываниями выше насчет строк и т.п. Действительно перегрузили язык уже и так. Пишу на с++ постоянно. Но сил уже нет изучать новые хитрости и премудрости. Qt очень и очень радует. Есть еще Poco Library для некоторых вещей. В принципе вот куда надо смотреть. Язык надо упрощать, чтобы было приятно на нем писать.
volkoff_roman
14.07.2016 11:26+1Определённо не хватает включенных в язык графических модулей, как в Борландовском Турбо Паскале. Ну чтобы новичкам было легче вливаться.
Ну и компоненты, как в Делфи.
Злой тролль-сишник так и не осиливший плюсы.
crackedmind
14.07.2016 11:43+6Радует что при использовании structured bindings можно будет проще по мапу итерироваться, а не ковырятся с first/second
for(auto [key,value]: my_map) {
}
Shamov
14.07.2016 11:44+1Не верится, что уже в следующем году всё это будет законно. Особенно мне нравится первый пункт — constexpr if. Я пока не знаю точно, где и как я это буду использовать, но уверен, что эта фича станет моим новым фаворитом.
RomanArzumanyan
14.07.2016 12:18+8Есть мнение — на собеседованиях вопросов прибавится. Код останется прежним.
ivann
14.07.2016 14:03Для каких переменных используется именование с подчеркиванием на конце? Например, valuesmutex.
Randl
14.07.2016 21:03+1Интересно что люди одновременно хоронят С++ за лишние фичи и за из отсутствие. Причем одни и те же фичи одновременно ругают и хвалят куча людей.
Кто не верит в комитет — вступайте и действуйте ИМХО. Кто не верит в С++ — развивайте/пользуйтесь альтернативами
totally_nameless
15.07.2016 12:20+6«Есть всего два типа языков программирования: те, на которые люди всё время ругаются, и те, которые никто не использует.»
— Bjarne Stroustrup.
Satus
15.07.2016 12:20-1Когда я читаю про новые стандарты С++, сначала я думаю «Вау, круто, этого мне и правда не хватало!», потом «Когда всё это учить?», а под конец «Как все эти данные держать в голове вообще?»
Tujh
15.07.2016 14:22+2Ищите положительные стороны, изучив всё эти фишки и умея их применить на практике эффективно можно стать очень ценным и высокооплачиваемым специалистом :)
semenyakinVS
15.07.2016 17:05-1А может кто-нибудь, кто лучше в разбирался в теме, рассказать о проблемах с концептами в плюсах? Просто я ещё понимаю, когда речь идёт о полноценном языке контроля AST — это понятно почему трудно. Но мне всегда казалось, что в концептах как в фиче нет ничего сложного хотя бы потому, что она никак не влияет на кодогенерацию (если не рассматривать возможные оптимизации, которые компиляторы смогут делать благодаря концептам). Фактически, это что-то вроде встроенного в язык редактора правил для статического анализатора… Или я ошибаюсь?
encyclopedist
15.07.2016 17:17+1Проблема не в реализации (реализация уже год как есть в GCC), а в том, что комитет не может никак окончательно договориться как это должно работать. Постоянно возникают новые возражения и соображения. Ну и хотят дождаться практического опыта с этой реализацией.
semenyakinVS
15.07.2016 17:37-1Ух ты, погуглил — точно! Где-то очень вскользь встречал что будут такое делать — но как-то мимо прошло, надо же. Спасибо, смотрю вот теперь чего там в gcc наделали. Первый же пример впечатляет — ограничение на перечень методов объекта-аргумента. Я так понимаю, так же и с шаблонным аргументами можно?
Быстрее бы до ума довели.
kdekaluga
19.07.2016 15:01+1Вот смотрю на «рост» С++ и всегда удивляюсь — почему такую простую вещь, как for… else никак не реализуют? Неужели это никому не надо?
Представим ситуацию поиска элемента где-либо (например, в массиве):
item a = ...; for (...) { item b = ...; if (a == b) { // We've found the item do_something (); break; } } // We are here in both cases when we've found <a> and when we haven't.
Без введения дополнительных проверок или использования goto различить причины «выхода» из цикла невозможно.khim
19.07.2016 15:53Как показала моя практика: программисты на Питоне (где эта конструкция есть, если не ошибаюсь, с самого начала) смотрят на тебя «квадратными глазами» (и просят «исправить отступы», что, понятно, всю логику изменит), когда ты эту конструкцию используешь. Не все, но многие.
Проблема курицы и яйца:for
...else
люди не используют потому что не знают, а раз не знают — то и в стандарт добавить её некому…
lemelisk
19.07.2016 16:08С новым синтаксисом, насколько я понимаю, можно будет написать как-нибудь так:
if (auto result_it = std::find(std::begin(container), std::end(container), a) ; result_it != std::end(container)) { do_something(); } else { ... }
kdekaluga
19.07.2016 16:51Да, если речь идет о простом поиске в контейнере — в stl есть варианты (даже сейчас). Но меня интересует именно конструкция языка. Например, я делаю итеративные расчеты с ограниченным количеством итераций (лимит в for), и если на какой-либо итерации я достигаю заданной точности, выхожу из цикла досрочно (break), если же за определенное количество итераций достигнуть требуемой точности не получается, я должен предпринять какое-либо еще действие. Понятно, что в таком примере один лишний if на ситуацию вряд ли повлияет, но по мне это именно тот случай, где задача едина для всех платформ и компиляторов и четко формализована.
lemelisk
19.07.2016 17:49Можно завернуть код в «фиктивную» лямбду c мгновенным вызовом и выходить не break'ом, а return'ом:
[&]{ for (...) { ... if (...) return; } // Else part ... }();
JIghtuse
19.07.2016 18:51Если можно завернуть поиск в функцию, можно (будет) использовать std::optional, у которого как раз семантика «значения может не быть».
kdekaluga
20.07.2016 18:31Можно, конечно. Варианты есть всегда)
Но для чего вводят if (init; cond)? Ведь и без этой конструкции варианты есть. Вот так же и тут)
Antervis
19.07.2016 20:43-1> Например, я делаю итеративные расчеты с ограниченным количеством итераций (лимит в for)
ну и итерируйтесь не до end(), а до advance(begin(),N). Для random access итераторов (например, в векторе) можно даже писать begin()+N.
Если там какая-то совсем сложная логика, то проще либо на функции разбить (и возвращаться через return), либо (на крайний случай) булевый флаг завести. И то вызовет меньше вопросов
А если уж и заводить такой функционал, то назвать как минимум не else, а interrupted/finished, напримерkdekaluga
20.07.2016 18:32Какое именно слово использовать для такой конструкции — не столь важно. Вопрос в том, почему бы ее не ввести вообще, если уж дошли до if (init; cond)
Antervis
21.07.2016 06:05разница в том, что if (init; cond) очень удобен, например, для раннего возврата, проверки защищенных мьютексами переменных (навскидку). И им гарантированно будут пользоваться. Что до for… else — я о существовании такой конструкции узнал только сейчас, хотя уже кодил на питоне
lemelisk
20.07.2016 22:10+2К слову, у вас есть идея как можно ввести эту фичу, не добавляя новых ключевых слов и при этом не сломав совместимость с подобным кодом:
if (...) for (...) one_code_line_action; else ...
kdekaluga
21.07.2016 02:52+1Справедливое замечание)
Навскидку могу предложить такие варианты:
1. Ввести новое ключевое слово. Не самый лучший вариант, однако, например, в С++11 появилось же auto. Вполне возможно, что раньше оно использовалось где-то в программах.
2. Использовать комбинацию ключевых слов, например:
for (...) {...} for else {...}
3. Использовать >> или ->. На мой взгляд, внешне будет неплохии решением:
for (int i = 0; i < 5; ++i) if (get_byte_from_stream () == value) break; >> send_byte_to_stream (value); ... for (int i = 0; i < 5; ++i) if (get_byte_from_stream () == value) break; -> send_byte_to_stream (value);
4. Использовать ключевое слово continue. По текущему синтаксису оно используется так:
continue;
Здесь же будет
continue statement for (int i = 0; i < 5; ++i) if (get_byte_from_stream () == value) break; continue send_byte_to_stream (value);
khim
21.07.2016 03:27Не самый лучший вариант, однако, например, в С++11 появилось же auto.
auto
было ключевым словом с 70х годов, однако.
Вы бы лучше бы какой-нибудьdecltype
вспомнили…
А насчёт комбинации ключевых слов можно своровать подход у Ады и использоватьor else
(или даже простоor
).
P.S. Вы не поверите, но иand
иor
и дажеnot
— в C++ являются ключевыми словами!kdekaluga
21.07.2016 03:36auto было ключевым словом с 70х годов, однако.
эх, забыл, каюсь) думал про decltype, но auto же короче)
Ну, на самом деле, сейчас (лично) мне нравится больше вариант continue либо >> (->). Но, по большому счету, это все равно ничего не изменит)
kdekaluga
21.07.2016 03:40Да, на счет or, and и not — все верно, в стандарте они есть. Но вот только, например, MSVC2012 ничего про них не знает, позволяя написать такое:
int or = 5; int and = or + 1; and++;
MSVC, конечно, далеко не истина в последней инстанции, но это означает, что где-то могут быть программы, использующие такие имена.
Sliver
20.07.2016 04:34+2- Выход из вложенных циклов без goto планируется?
- Планируется ли 'continue' блок для for/while, опять же, чтобы избежать goto и отличить нормальный выход из цикла от break?
kdekaluga
21.07.2016 03:04Согласен, тот же break сейчас используется без параметров, можно было бы ввести
break n;
где n — число вложенных циклов, из которых надо выйти (и, например, 0 — для выхода из всех циклов, сколько бы их не было).Antervis
21.07.2016 07:39+1так это сложнее читать чем goto
kdekaluga
21.07.2016 13:42+1Я лично (так как начинал программировать с ассемблера) никогда против goto не был)
Когда стал изучать С++ (С), мне достаточно успешно внушили, что лучше заводить флаг и пару ифов, но только не goto (что типа компилятор разберется и оптимизирует, а читать будет легче). Потом посмотрел исходники больших проектов и увидел goto там. С тех пор использую сам при необходимости без особых зазрений совести. Но встречал людей, для которых goto — как начало третьей мировой войны.khim
21.07.2016 15:55-1Но встречал людей, для которых goto — как начало третьей мировой войны.
Угу. При этом те же самые люди без зазрения совести используют,switch
,break
,continue
— и канючат на тему «расширения их функциональности».
Карго культ какой-то. Как будто проблемаgoto
— не в усложнении логики программы, а в том, что это слово портит карму.
Да,goto
следует избегать, так как они запутывают логику — но компилятору они ничуть не мешают (он всё равно все ваши циклы превращает в базовые блоки иgoto
), а запутать логику работы флажки могут похлеще любогоgoto
. Собственно любая программа с любым количествомgoto
может быть превращена в программу безgoto
добавлением одной переменной (next_basic_block
, ага), что и показывает ущербность подхода с флажками.
Ещё раз, для тех кто в танке: «программа с одним, всего лишь одним флажком (правда многопозиционным) может быть также сложна в отладке, как любая программа с любым количествомgoto
». Прэлестно, правда?Idot
22.07.2016 11:53Поход против goto был начат в идеологии Структурного Программирования, а Кнут проблемы в использовании goto — не видел.
DarkEld3r
21.07.2016 11:15+2Это ещё рефакторить менее удобно. Добавился цикл — надо будет посмотреть не используются ли внутри break/continue n. Лучше уж с именованными метками.
easimonenko
20.07.2016 19:18По-поводу конструкции
if (init; condition) ...
. Почему не сделали как в некоторых других языках, например, вот так:let { init; } if (condition) ...
илиif (condition) ... where { init; }
?Sliver
21.07.2016 07:35+3Неясно, чем
let { init; } if (condition) { ... }
хоть немного лучше, чем сегодняшнее
{ init; if (condition) { ... } }
Второе семантически более соответствует сегодняшним представлениям об области видимости и времени жизни переменных.
А
where
в хвосте — вообще жуть.
Это надо будет мотать в конец блока чтобы посмотреть, что же там за переменные в начале.
Azoh
21.07.2016 12:20Тогда было бы логичнее сделать отдельное выражение
let (init) something
, которое было бы семантически было бы эквивалентно{ init; something }
Тогда можно было бы использовать его не только для if и while, а в любом подобном месте. Скажем, в примере с захватом мьютекса мне может и не надо ничего проверять, я просто хочу захватить мьютекс, выполнить какое-то действие, а потом мьютекс отпустить.
let (std::lock_guard<std::mutex> lock(m)) if (!container.empty()) { // do something }
let (std::lock_guard<std::mutex> lock(m)) { // do something }
lemelisk
21.07.2016 14:53+2Вторая строчка эквивалентна простому:
{ std::lock_guard<std::mutex> lock(m); // do something }
Цель же введения этого нового способа записи в стандарт в банальной экономии строк кода.
FlexFerrum
21.07.2016 18:13Тут же в другом фишка. Иногда (и не так редко, как кажется) требуется, чтобы область действия lock'а совпадала с областью действия оператора if. Находилась в рамках его скоупа. Любой «двухстрочный» эквивалент не соответствует этой хотелке. Вот другой пример на это же:
if (ResultCode rc = DoSomething(); rc.Failed()) { // обрабатываем ошибку, отваливаем }
В рамках текущего стандарта это выглядит так:
ResultCode rc; if ((rc = DoSomething()).Failed()) { // Обрабатываем ошибку }
Довольно, как это говорят, ugly. Предвосхищая вопрос. Такие конструкции могут использоваться в библиотечных «обёртках» над стандартной обработкой ошибок, логированием и т. п. То есть типа:
CHECK_SUCCEEDED_RETURN(DoSomething()) << «Приключилась адская хрень»;
sborisov
22.07.2016 11:38«Автоматическое определение шаблонных параметров для классов»
интересно, как выкрутились, т.к. Страуструп декларировал:
Язык программирования С++ 386 стр.
«Обратите внимание, что параметры шаблона класса (в отличие от шаблона функ-
функции) никогда не выводятся. Причина заключается в том, что гибкость, обеспечивае-
обеспечиваемая наличием нескольких конструкторов класса, во многих случаях является непреодолимым
препятствием на пути такого выведения, и еще в большем числе случаев
выведение неоднозначно. Специализация предоставляет механизм неявного выбора
из различных реализаций класса (§ 13.5).»sborisov
22.07.2016 11:41Или из 4го издания стр. 686
Note that class template parameters are never deduced. The reason is that the flexibility pro-
vided by several constructors for a class would make such deduction impossible in many cases and
obscure in many more. Instead, specialization (§25.3) provides a mechanism for implicitly choos-
ing between alternative definitions of a template. If we need to create an object of a deduced type,
we can often do that by calling a function to do the deduction (and creation). For example, con-
sider a simple variant of the standard library’s make_pair() (§34.2.4.1):
template<typename T1, typename T2>
pair<T1,T2> make_pair(T1 a, T2 b)
{
return {a,b};
}
auto x = make_pair(1,2);
auto y = make_pair(string(«New York»),7.7);
// x is a pair<int,int>
// y is a pair<str ing,double>
DistortNeo
22.07.2016 15:43+1Выкрутились вот так:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r2.html#uec
Грубо говоря, принцип аналогичен вызову функций, только для конструкторов.
Необходимость же такой возможности возникла из-за лямбд, тип которых в явном виде записать невозможно.
mezastel
Хочется думать, что рано или поздно у них руки дойдут и до модулей. Серьезно, я готов недополучить всех этих плюшек, да даже ranges и concepts, лишь бы сделали модули.