Название: static_cast элементов в контейнере Отправлено: Akon от Январь 18, 2015, 16:29 Для простоты рассмотрю только upcast. Производный тип есть базовый тип (при открытом наследовании):
Код: class Base {}; Тем не менее, подобный сценарий не работает для контейнеров, поскольку конечные типы (инстанцированные контейнеры) не связаны отношением открытого наследования: Код: std::vector<Derived*> ds; Убрать ошибку можно лишь грубым методом - reinterpret_cast: Код: std::vector<Base*>& bs = reinterpret_cast<std::vector<Base*>&>(ds); Собственно, мне хочется как-то так: Код: std::vector<Base*>& bs = container_items_static_cast<Base*>(ds); // ОК Есть ли где-то что-то подобное (в бусте, может, - давно туда не заглядывал) с необходимыми проверками, с перегрузкой под константность? Название: Re: static_cast элементов в контейнере Отправлено: __Heaven__ от Январь 19, 2015, 00:08 Мне кажется, что это какой-то хард кор.
На мой взгляд, необходимо ручками перетащить элементы из одного контейнера в другой. Ну или написать функцию, если хочется сделать это в одну строчку. То, что вы пытаетесь сделать, выглядит как-то грубо и по сути вы преобразуете class Heaven в class Earth. То, что это как-то работает через reinterpret cast - заслуга std::vector<void*>. Но это просто мое мнение. Название: Re: static_cast элементов в контейнере Отправлено: Akon от Январь 19, 2015, 10:38 Это как раз для того, чтыбы избежать "необходимо ручками перетащить элементы из одного контейнера в другой. Ну или написать функцию, если хочется сделать это в одну строчку.". Смысл в отсутствии ненужных вычислений - это же С/С++, а не Java или C# :)
Я прекрасно понимаю, что происходит "за кулисами". Осознанно пользуюсь этим подходом. Название: Re: static_cast элементов в контейнере Отправлено: Igors от Январь 19, 2015, 10:46 Мне кажется, что это какой-то хард кор. "Простое" решение - работать с vector <Base *>, но тогда выплывает масса приведений после взятия эл-та. А делать везде vector <Derived *> еще хуже, все кишки наружу. Я унаследовался от контейнера и сохранил в нем ID типа явно - чтобы отловить недопустимое приведение в runtime. Но все равно привожу Название: Re: static_cast элементов в контейнере Отправлено: ViTech от Январь 19, 2015, 13:13 Насколько я понял:
Код вполне себе нормальное решение, только хочется, чтобы были дополнительные проверки на допустимость такого преобразования. За буст не скажу, но можно попробовать написать container_items_static_cast самому, взяв в качестве основного преобразования reinterpret_cast, а проверку типов выполнять с помощью type_traits (http://www.cplusplus.com/reference/type_traits/). Название: Re: static_cast элементов в контейнере Отправлено: Igors от Январь 19, 2015, 13:32 За буст не скажу, но можно попробовать написать container_items_static_cast самому, взяв в качестве основного преобразования reinterpret_cast, а проверку типов выполнять с помощью type_traits (http://www.cplusplus.com/reference/type_traits/). Не вижу как это сработает если контейнер пуст. А непустой - так проще банальное dynamic_castНазвание: Re: static_cast элементов в контейнере Отправлено: Akon от Январь 19, 2015, 15:01 Разумеется, дополнительные проверки необходимы, а точнее, требуется только, чтобы "... Base есть "первая база" Derived". Когда-то (2-3 года назад) наспех я сделал общее решение (на шаблонах) проверки первой базы, но оно, если мне не изменяет память, работало в рантайме, и type_traits тогда мне не помог. Сейчас в фоне я повторно интересуюсь проблемой.
Название: Re: static_cast элементов в контейнере Отправлено: _Bers от Январь 21, 2015, 08:38 Собственно, мне хочется как-то так: Код: std::vector<Base*>& bs = container_items_static_cast<Base*>(ds); // ОК Честных способов не существует. Нечестные нарушают инкапсуляцию вектора, и аннулируют его инвариант (он вам больше ничего не гарантирует). вполне себе нормальное решение, только хочется, чтобы были дополнительные проверки на допустимость такого преобразования. Нет ничего нормального в грубых хаках, которые превращают код в мину замедленного действия. Подобного рода функционал (с проверками) может предоставить только сам шаблон вектора, и больше никто. Это связанно с тем, что инстансы с разными параметрами шаблона могут различаться, как небо и земля (допустим в угоду каких то оптимизаций). И нужно точно понимать его внутреннее устройство, требования и ограничения, что бы реализовать безопасный каст. Любая реализация снаружи - только на свой страх и риск. Название: Re: static_cast элементов в контейнере Отправлено: Akon от Январь 21, 2015, 10:33 Способ-хак с соответствующей проверкой (см. выше) как-раз гарантирует инвариант по указателю. Я рассматриваю в контейнере только указатели на объекты (см. выше), и в этом случае существенно:
1. размер эл-та контейнера остается тем-же; 2. проверка проверяет, что при касте не происходит смещения this. Название: Re: static_cast элементов в контейнере Отправлено: ViTech от Январь 21, 2015, 13:58 Можно попробовать начать с таких конструкций (набросал на довольно скорую руку и не тестировал).
Определяем общий шаблон для преобразования контейнеров: Код
Далее специализируем его для более конкретных контейнеров, например, вида container<_Type, _Alloc> (vector, list и т.п.).: Код Здесь проверки нужно изменить под свои требования. Немного синтаксического сахара, чтобы типы контейнеров каждый раз не писать. И для уверенности, что преобразуются контейнеры одного типа: Код
Примеры использования: Код
Нет ничего нормального в грубых хаках, которые превращают код в мину замедленного действия. В целом согласен, с хаками нужно быть осторожными. Но имеет ли право на существование преобразование... И нужно точно понимать его внутреннее устройство, требования и ограничения, что бы реализовать безопасный каст. Любая реализация снаружи - только на свой страх и риск. Код если есть гарантии, что для всех элементов выполняется условие static_cast<Base*>(derived)? И как относиться к контейнеру, который указатели на базовые и производные объекты хранит по разному? В любом случае, гарантии должны подтверждаться тестами :). Название: Re: static_cast элементов в контейнере Отправлено: Akon от Январь 21, 2015, 18:54 ViTech, спс. за интерес и конкретное предложение.
Код: const bool is_compatible_containers = C контейнером работать не нужно, нужно работать с элементом контейнера. В контейнере в качестве элемента всегда указатель (по условию задачи). Далее, реинтерпрет будет безопасен тогда, когда не происходит смещения this (статик_каст осуществляет такое смещение). В рантайме это проверить элементарно, не знаю, возможно ли это в компайл-тайм. Название: Re: static_cast элементов в контейнере Отправлено: ViTech от Январь 21, 2015, 20:42 Код: const bool is_compatible_containers = По static_cast элементов - это отдельный вопрос. По идее, в компайл-тайм никаких конкретных указателей нет, следовательно и сравнивать нечего. Разве что завести какие-нить константные объекты. Если ещё имеет место множественное наследование, то я пока type_traits не так хорошо знаю, чтобы проверить иерархию необходимым образом. Но если уже начали писать хаки, то можно родить, например, следующий ужасный код: Код Страшно? :) Да. Но в некоторых случаях работает. В частности на g++ 4.8.2. И даже ошибку правильную выдаёт в строке со static_cast, если оно не выполняется. А вот msvc 2012 такое собирать не хочет, возможно потому, что ещё не знает constexpr. Название: Re: static_cast элементов в контейнере Отправлено: _Bers от Январь 21, 2015, 20:43 Способ-хак с соответствующей проверкой (см. выше) как-раз гарантирует инвариант по указателю. Я рассматриваю в контейнере только указатели на объекты (см. выше), и в этом случае существенно: 1. размер эл-та контейнера остается тем-же; 2. проверка проверяет, что при касте не происходит смещения this. Куда смотреть, на это: Код: std::vector<Base*>& bs = reinterpret_cast<std::vector<Base*>&>(ds); Потому что эта хрень - мина замедленного действия. После применения reinterpret_cast любые проверки уже избыточны. Вам уже никто ничего не гарантирует. Более того: сам reinterpret_cast никому ничего не гарантирует. После его использования обеспечить гарантии становится невозможным. Вам нужно понять одну вещь: вы не тип элемента кастите. Вы тип самого вектора кастите к принципиально отличному типу. Две вектора с разными типами по своему строению могут различаться, как небо и земля. Все последствия только на вашей совести. Название: Re: static_cast элементов в контейнере Отправлено: Old от Январь 21, 2015, 20:49 Две вектора с разными типами по своему строению могут различаться, как небо и земля. Достаточно вспомнить о: vector<bool> и vector<T>,где T - любой, исключая bool. Название: Re: static_cast элементов в контейнере Отправлено: Igors от Январь 28, 2015, 08:33 Две вектора с разными типами по своему строению могут различаться, как небо и земля. Не понимаю чем/как они могут отличаться. Вектор должен быть совместим с массивомКод
Ну один тип указателя вместо другого, и что? Можно наворотить пример с множ наследованием где будет вылетать, но это надо постараться. А так не вижу чего же опасаться Достаточно вспомнить о: vector<bool> и vector<T>, Расскажите, не читал :)где T - любой, исключая bool. Др дело что это как-то "нездорово", нет никакой защиты от добавления указателя на базовый - ну автор понимает этот риск Название: Re: static_cast элементов в контейнере Отправлено: Old от Январь 28, 2015, 10:17 Расскажите, не читал :) std::vector<bool> хранит каждое значение в отдельном бите, т.е. его внутреннее устройство кардинально отличается std::vector<T>. Специализированные методы соответственно заточены для такого хранения. Если попробовать привести вектор bool к какому нибудь vector<char>, то получиться "каша".Так же я могу специализировать любую коллекцию. Например, vector<Base*> будет стандартной реализации, а vector<Derived*> моей собственной, не имеющей ничего общего со стандартной. Название: Re: static_cast элементов в контейнере Отправлено: __Heaven__ от Январь 28, 2015, 10:57 vector<Base*> и vector<Derived*> генерируют два совершенно разных класса. Только то, что они оба унаследованы от vector<void*> не приводит к ошибке.
Название: Re: static_cast элементов в контейнере Отправлено: Old от Январь 28, 2015, 11:10 vector<Base*> и vector<Derived*> генерируют два совершенно разных класса. Только то, что они оба унаследованы от vector<void*> не приводит к ошибке. Как правило, устройство и внутренности у них одинаковы, поэтому и не приводит к ошибке. А я могу так специализировать вектор, что устройство и внутренности у него будут совершенно другие.Название: Re: static_cast элементов в контейнере Отправлено: Igors от Январь 28, 2015, 12:31 std::vector<bool> хранит каждое значение в отдельном бите, т.е. его внутреннее устройство кардинально отличается std::vector<T>. Не знал об этом, спасибо Так же я могу специализировать любую коллекцию. Например, vector<Base*> будет стандартной реализации, а vector<Derived*> моей собственной, не имеющей ничего общего со стандартной. Это предполагает непосредственное участие "злобного Буратино" - а без этого все одинаково, можно сказать "байт в байт". sizeof одинаков, конструктор/деструктор для указателей одинаков. Поэтому опасения преувеличены, хотя конечно конструкция "с душком"vector<Base*> и vector<Derived*> генерируют два совершенно разных класса. Только то, что они оба унаследованы от vector<void*> не приводит к ошибке. Во всяком случае здесь "унаследованы" - неверный терминНазвание: Re: static_cast элементов в контейнере Отправлено: __Heaven__ от Январь 28, 2015, 13:05 Во всяком случае здесь "унаследованы" - неверный термин Согласен.Вот записки одного злого гения! (http://i9.pixs.ru/storage/1/3/1/Straustrup_1170578_15796131.png) (http://i9.pixs.ru/storage/1/3/7/Straustrup_9123757_15796137.png) Название: Re: static_cast элементов в контейнере Отправлено: __Heaven__ от Январь 28, 2015, 13:08 А я могу так специализировать вектор, что устройство и внутренности у него будут совершенно другие. А вы про указатели говорите? Как это сделать?Название: Re: static_cast элементов в контейнере Отправлено: Old от Январь 28, 2015, 13:54 А вы про указатели говорите? Как это сделать? Да как и с любым другим типом: Код
Цитировать vec::vec( T ) vec::vec( T ) vec::vec( char * ) Название: Re: static_cast элементов в контейнере Отправлено: __Heaven__ от Январь 28, 2015, 14:07 Я подумал, что вы про класс вектор из библиотеки.
Название: Re: static_cast элементов в контейнере Отправлено: Old от Январь 28, 2015, 14:11 Я подумал, что вы про класс вектор из библиотеки. ::) Код
Название: Re: static_cast элементов в контейнере Отправлено: __Heaven__ от Январь 28, 2015, 14:12 ::) Я подозревал, что это последует :)Название: Re: static_cast элементов в контейнере Отправлено: Igors от Январь 28, 2015, 15:27 Код
Название: Re: static_cast элементов в контейнере Отправлено: Old от Январь 28, 2015, 15:33 Правильно ли я понял что эта реализация НЕ может использовать ничего из vector <T>? (все требуемые методы надо писать самому) Если вы внутри все делаете по своему, то конечно все методы (точнее все используемые методы) придется делать самому.Название: Re: static_cast элементов в контейнере Отправлено: Akon от Январь 28, 2015, 20:20 Код: const bool is_compatible_containers = Цитировать const int dummy_object = 444555; Такого рода подход я и использовал для рантайм проверки. Уже не помню, почему не удалось в компайл-тайм. Уменя тогда были gcc 4.6 и msvc2010. Возможно, с gcc был компайл-тайм._SourceItem const source_item = (_SourceItem) dummy_object; _DestItem const dest_item = static_cast<_DestItem> (source_item); const bool is_compatible_inheritance = (int) dest_item == dummy_object; static_assert(is_compatible_inheritance, "Incompatible inheritance"); Друзья, я полностью согласен с тем, что частичной или полной специализацией можно изменить потроха контейнера, в результате реинтерпрет потеряет всякий смысл. Но я рассматриваю релевантные практике варианты. Зайду с другой стороны - как вы реализуете метод MyContainer::items() в этом примере: Код: class Contaier; Название: Re: static_cast элементов в контейнере Отправлено: Igors от Февраль 01, 2015, 14:15 Код: // ну а как вы поступите здесь с Container::items()? Код Провериться в compile/run-time по-моему недостижимо если базовый класс не полиморфный (vector<Item*>). |