Здравствуйте, Marty, Вы писали:
M>>>Хочу вот сделать свой класс строк, который предоставляет всё то, что даёт std::basic_string. Вот буквально пару методов добавить, при том, что всё новое будет работать через методы std::basic_string'а. BFE>>Что конкретно?
M>trim/split, например
В других языках есть "extension methods" например, которые позволяют добавлять методы в класс без его (класса) изменения (то есть, добавить "снаружи").
Для С++ вроде бы это обсуждалось под названием "unified call syntax", но было забраковано по ряду причин.
Не вижу смысла идти против течения, я бы использовал обычные функции и не выделывался.
M>>>Отнаследоваться от std::basic_string'а, или сделать его членом класса, и написать/скопировать простыню делегирования? BFE>>И всё это ради двух методов?
M>Пока два, да. Потом можно добавить по вкусу
Здравствуйте, bnk, Вы писали:
bnk>В других языках есть "extension methods" например, которые позволяют добавлять методы в класс без его (класса) изменения (то есть, добавить "снаружи").
Угу, есть ещё Unified functions call (вроде бы так называется), где записи a.b(c) и b(a,c) — эквивалентны, но пока этого нет
bnk>Для С++ вроде бы это обсуждалось под названием "unified call syntax", но было забраковано по ряду причин. bnk>Не вижу смысла идти против течения, я бы использовал обычные функции и не выделывался.
Если писать всегда с именем NS — то длинно. Если сделать using NS/using NS::name — можно получить ошибку компиляции и/или не тот вызов. А метод объекта — он всегда метод объекта
M>>Пока два, да. Потом можно добавить по вкусу
bnk>Это "потом" никогда не наступит.
Или да. Ещё starts_with/ends_with, например. Что-то такое вроде уже наконец вошло в 20ый стандарт, но что-то меня это не радует, по скорости принятия фич. Я вот хочу такую строку, где бы я мог дописывать свои методы, но база была бы написана за меня, пусть она SSO/COW или ещё как-то оптимизирована, меня не должно парить, я хочу, чтобы моя строка работала и как питоновская строка по интерфейсу, и как QString, и как что-то ещё, чтобы хотя бы мои наработки из питона или кутишного кода можно было бы собрать с минимумом перделок
Здравствуйте, Marty, Вы писали:
M>Или да. Ещё starts_with/ends_with, например. Что-то такое вроде уже наконец вошло в 20ый стандарт, но что-то меня это не радует, по скорости принятия фич. Я вот хочу такую строку, где бы я мог дописывать свои методы, но база была бы написана за меня, пусть она SSO/COW или ещё как-то оптимизирована, меня не должно парить, я хочу, чтобы моя строка работала и как питоновская строка по интерфейсу, и как QString, и как что-то ещё, чтобы хотя бы мои наработки из питона или кутишного кода можно было бы собрать с минимумом перделок
Здравствуйте, bnk, Вы писали:
bnk>Выход есть — переходи на си шарп тайпскрипт
Там везде ограничений гораздо больше, чем в плюсиках, конкретно тайпскрипт, правда, не трогал, но вообще показалось — все тащат к себе фичи из плюсиков, это факт. Плюсики тоже, не спорю, сахарок из других языков к себе подвозят
Здравствуйте, sergii.p, Вы писали:
SP>так причём здесь final и виртуальный деструктор?
Я отвечал вот на это: AS>У всех стандартных базовых классов есть ложечка дерьма — отсутствие виртуальных деструкторов. Потому и не часто от них любят наследоваться.
Соответственно, мое мнение, что виртуальный деструктор не нужен.
А финал нужен, что явно запретить наследование и не вводить в искус.
SP>Ну не надо держать std::unique_ptr<std::vector>. Но никто вроде и не стремится к такому, когда наследуется от вектора.
Обычно, уже есть код который работает с std::string или std::vector.
Потом делают какой-нибудь MyString или MyVector.
И проблема в рефакторинге существующего кода не только в виртуальном деструкторе.
Как минимум будет срезка типа при присваивании или передаче по значению.
auto s1 = MyString("abcd"); // ok
std::string s2 = MyString("abcd"); // срезка
Ну и с деструктором все места отловить будет сложно.
Только глазками. Компилятор все устраивает.
auto p1 = std::make_unique<MyString>("abcd"); // ok
std::unique_ptr<std::string> p2 = std::make_unique<MyString>("abcd"); // UB
Здравствуйте, qaz77, Вы писали:
Q>Соответственно, мое мнение, что виртуальный деструктор не нужен. Q>А финал нужен, что явно запретить наследование и не вводить в искус.
ок, понял. С первым соглашусь, со вторым — категорически нет.
Q>Как минимум будет срезка типа при присваивании или передаче по значению. Q>
здесь не будет срезки. MyString& конвертится в string& и передаётся в конструктор копирования.
Q>Ну и с деструктором все места отловить будет сложно. Q>Только глазками. Компилятор все устраивает. Q>
такой код просто бессмысленен. Строка и вектор сами по себе указатели. Если такой код есть, это проблема программистов, даже не языка.
Ну и UB там тоже не будет. unique_ptr<MyString> прекрасно сконвертится в p2. Да и удаление пройдёт удачно. Вызовется деструктор std::string без ~MyString. Но если MyString — просто обёртка без данных, то всё ок.
В общем даже если хотеть навредить, это сделать очень сложно
Удаление по указателю на базовый класс сделает больно и непонятно, а это не только обычный но и умные указатели.
В общем класс от которого наследоваться не следует ибо мина.
Здравствуйте, sergii.p, Вы писали:
SP>Да и удаление пройдёт удачно. Вызовется деструктор std::string без ~MyString. Но если MyString — просто обёртка без данных, то всё ок.
Вот это UB по стандарту, если нет виртуального деструктора.
Здравствуйте, qaz77, Вы писали:
Q>Вот это UB по стандарту, если нет виртуального деструктора.
доказательства в студию. Компилятор видит указатель на std::string. Он знает адрес деструктора. Дальше просто конвертирует MyString& в std::string& и передаёт в деструктор. "Вижу цель, не вижу препятствий!"
Здравствуйте, sergii.p, Вы писали: SP>доказательства в студию. Компилятор видит указатель на std::string. Он знает адрес деструктора. Дальше просто конвертирует MyString& в std::string& и передаёт в деструктор. "Вижу цель, не вижу препятствий!"
C++ standard 5.3.5/3:
In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
Здравствуйте, qaz77, Вы писали:
Q>Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс.
Для контейнерных типов это несущественно
Q>Вот мне такой непродуктивный расход нафиг не нужен на таких мелких и везде употребимых классах.
Тут ключевое слово "тебе". А многим людям нужно. И ещё вопрос кого больше — кому "не нужно" или кому "нужно"?
В принципе уже давно назрела в сообществе потребность в нормальной (некоторые назовут её расширенной) стандартной библиотеке.
Как я вижу — нужно оставить старую библиотеку как V1 — будет использоваться всякими embeded и вирусописателями.
Но необходима новая библиотека/ки, которая покрывает все современные потребности для разработки.
Здравствуйте, AeroSun, Вы писали:
Q>>Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс. AS>Для контейнерных типов это несущественно
Такой подход противоречит плюсовой идиоме про "не платишь за то, что не используешь".
Вектор обычно размером в 3 указателя, а тут 4-й подъехал, sizeof на 25% больше.
А для строк тем более расход чувствителен, причем эту часть sizeof SSO не сможет использовать.
AS>В принципе уже давно назрела в сообществе потребность в нормальной (некоторые назовут её расширенной) стандартной библиотеке. AS>Как я вижу — нужно оставить старую библиотеку как V1 — будет использоваться всякими embeded и вирусописателями. AS>Но необходима новая библиотека/ки, которая покрывает все современные потребности для разработки.
Уже было такое, когда все подряд наследуется от одного предка с виртуальным деструктором.
У Borland был TObject основной смысл которого в полиморфном удалении любого наследника.
Такой тотальный ООП впоследствии вызывал немало критики...
По моему мнению, в стандартной библиотеке C++ наследование уместно только для std::exception и его стандартных наследников.
Сам принцип работы catch нам навязывает наследование.
А для других классов STL — это дичь и разбрасывание граблей.
При условии, что MyString public наследник std::string скомпилируется.
Стандартный подход при реализации фабрик, когда на выходе unique_ptr для интерфейса, а make_unique для реализаций.
Здравствуйте, qaz77, Вы писали:
Q>Здравствуйте, AeroSun, Вы писали:
Q>>>Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс. AS>>Для контейнерных типов это несущественно Q>Такой подход противоречит плюсовой идиоме про "не платишь за то, что не используешь".
Ну, вся эта идиома крутится вокруг компромиссов. И я бы сказал в плюсах немало спорных решений.
Я бы ещё принял если бы запросы разработчиков на стандартные вещи удовлетворялись комитетом, но как видим следуя ей комитет не в состоянии родить ничего без переламывания его через колено.
Q>Вектор обычно размером в 3 указателя, а тут 4-й подъехал, sizeof на 25% больше.
Для контейнерных типов — это ни о чём, микроскопическое изменение по сравнению с обычным его размером.
Q>А для строк тем более расход чувствителен, причем эту часть sizeof SSO не сможет использовать.
Для строк — это тоже ни о чём, SSO не нужен.
Ещё раз, про embeded и вирусописателей я не говорю — им можно оставить старую версию библиотеки.
Q>Уже было такое, когда все подряд наследуется от одного предка с виртуальным деструктором. Q>У Borland был TObject основной смысл которого в полиморфном удалении любого наследника. Q>Такой тотальный ООП впоследствии вызывал немало критики...
При чём тут тотальный ООП? Мы о другом
Q>По моему мнению, в стандартной библиотеке C++ наследование уместно только для std::exception и его стандартных наследников. Q>Сам принцип работы catch нам навязывает наследование.
Q>А для других классов STL — это дичь и разбрасывание граблей.
Тут ты не прав. Есть куча случаев когда это нужно.
Вот к примеру из моего недавнего: делаю крипто библиотеку, нужен контейнер (вектор/дек/строка) который ведёт себя полностью как стандартный. Необходимое условие — при умирании объекта он обязан затереть свою память.
Так вот в текущих плюсах с текущей стандартной библиотекой эта задача не решаема без костылей.
Да, я на всяк случай прошерстил все крипто библиотеки и вот такие результаты в зависимости от библиотеки:
1) Вручную вызывают затирание перед уничтожением объекта. Это костыль — так как легко забыть это сделать
2) Реализовывают свой STL-совместимый контейнер. И я скажу это не тривиальная задача. Это костыль — стандартные средства не помогают
3) Делают свой объект в который включают вектор. Экспозят либо часть интерфейса либо полный интерфейс стандартного контейнера. Это костыль по очевидным причинам.
А всего-то нужно было отнаследоваться и переопределить деструктор.
При таких недостатках — то же SSO ну не интересен вообще никак.
Если такие специфические требования то нужно писать свой контейнер. Заодно на уровне типов защитишься от того, что кто-то передаст его как вектор и мувнет куда-нибудь.
AS>А всего-то нужно было отнаследоваться и переопределить деструктор.
И что? При росте размера вектора у тебя по всей памяти будут расползаться копии твоих секретных данных при переаллокации буфера Нафиг такой говнокод.
Здравствуйте, qaz77, Вы писали:
Q>Здравствуйте, AeroSun, Вы писали: AS>>Для строк — это тоже ни о чём, SSO не нужен.
Q>Ну тогда тебе в джаву или сишарп.
Зачем?
Перефразирую вопрос: где кроме embeded нужен SSO?
Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке?
Ответ: ну практически нигде
Здравствуйте, AeroSun, Вы писали:
AS>Зачем? AS>Перефразирую вопрос: где кроме embeded нужен SSO? AS>Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке? AS>Ответ: ну практически нигде
struct Person
{
std::string f;
std::string i;
std::string o;
};
void foo(const Person& some_person)
{
Person copy_of_person = some_person; // только одна дорогая аллокация для копирования длинного отчества
// do something with copy_of_person...
}
int main()
{
Person p;
p.f = "Иванов"; // SSO works!
p.i = "Иван"; // SSO works!
p.o = "Владиславович"; // SSO don't works((
foo(p);
return 0;
}
Здравствуйте, AeroSun, Вы писали:
AS>Зачем? AS>Перефразирую вопрос: где кроме embeded нужен SSO? AS>Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке? AS>Ответ: ну практически нигде
Я, например, делал Decimal, который хранит десятичные цифры числа. В качестве хранилища использовал vector/string, так вот при использовании string скорость в несколько раз была выше на типичных операциях