Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Январь 28, 2021, 13:04



Название: Итераторы
Отправлено: Igors от Январь 28, 2021, 13:04
Добрый день

Есть большой класс, и в нем
Код
C++ (Qt)
struct CBigClass {
...
QList<CData> mData;
QSet<CData *> mSelection;
...
};
И есть метод
Код
C++ (Qt)
void CBigClass::SomeMethod( void )
{
 ..
 for (auto & data : mData) {
  ...
 }
}
С довольно содержательным телом цикла, переписывать его явно не хочется. (как и делать из тела метод)

Требуется сделать так чтобы можно было крутить цикл и по selection,
Код
C++ (Qt)
void CBigClass::SomeMethod2( void )
{
 ..
 for (auto it : mSelection) {
  ...
 }
}
Но конечно без копирования содержательной части (тела цикла).
Как это сделать итераторами или чем еще (в "современном С++") ?

Спасибо


Название: Re: Итераторы
Отправлено: Авварон от Январь 28, 2021, 14:15
Ш - шаблоны


Название: Re: Итераторы
Отправлено: RedDog от Январь 28, 2021, 16:53
Код:
for (auto it = std::begin(container); it != std::end(container); ++it)
Останется только container через шаблон передать
Давно не видел void в скобочках.


Название: Re: Итераторы
Отправлено: Igors от Январь 29, 2021, 08:38
Код:
for (auto it = std::begin(container); it != std::end(container); ++it)
Останется только container через шаблон передать
Давно не видел void в скобочках.
Так контейнеры имеют разные типы эл-тов, как бум извращаться?

Конечно "цена вопроса" невелика, есть неск приемлемых решений. Самое простое - конвертировать контейнеры в вектор указателей std::vector<CData *>. Ну будет какой-то оверхед, но не смертельный. Можно и просто "заткнуть" парой-тройкой  if'ов, напр
Код
C++ (Qt)
void CBigClass::SomeMethod( bool useSelection )
{
 ..
 int limit = useSelection ? mSelection.size() : mData.size();
 auto dataIt = mData.begin();  
 auto selIt = mSelection.begin();  
 for (int i = 0; i < limit; ++i) {
   CData * dataPtr;
   if (useSelection) {
      dataPtr = *selIt;
      ++selIt;
   }
   else {
      dataPtr = &(*dataIt);
      ++dataIt;
   }
 
  ...
 }
}
Это конечно явно "не общий случай", а вдруг надо еще какой-то контейнер парить? С др стороны пока не видно откуда ему взяться, все CData во владении CBigClass

Просто бросается в глаза что нужно "итерироваться" причем всяко-разно. Нет ли чего-нибудь модного/элегантного ?


Название: Re: Итераторы
Отправлено: RedDog от Январь 29, 2021, 09:53
std::advance
std::next


Название: Re: Итераторы
Отправлено: Igors от Январь 29, 2021, 11:18
std::advance
std::next
Не вижу как это поможет разрулить разные типы эл-тов. Если с темплейтами то может так "подмазать"
Код
C++ (Qt)
template<class Iter>
CData & DataRef( Iter & it )
{
 return *it;
}
 
CData & DataRef( QSet<CData *>::iterator & it )
{
 return *(*it);
}
И вызывать
Код
C++ (Qt)
template<class Container>
void CBigClass::SomeMethod( Container & container )
{
 ...
 for (auto it = container.begin(); it != container.emd(); ++it) {
  CData & data = DataRef(it);
  ...
 }
}
Попинайте


Название: Re: Итераторы
Отправлено: RedDog от Январь 29, 2021, 11:23
SomeMethod сделать шаблонным, шаблоном туда передавать контейнер


Код:
template<typename T>
void iterateContainer(const T& t)
{
    for (auto it = std::begin(t); it != std::end(t); ++it)
    {
        // do something
    }
}

void testIterate()
{
    std::vector<int> vec;
    std::list<int> list;
    std::map<int, std::string> map;
    iterateContainer(vec);
    iterateContainer(list);
    iterateContainer(map);
}


Название: Re: Итераторы
Отправлено: Igors от Январь 29, 2021, 12:37
Код:
template<typename T>
void iterateContainer(const T& t)
{
    for (auto it = std::begin(t); it != std::end(t); ++it)
    {
        // do something
    }
}
}
Do something with what? :) Как я использую it ? Ведь рвзыменование *it даст разные типы для разных входных контейнеров.


Название: Re: Итераторы
Отправлено: RedDog от Январь 29, 2021, 13:14
Касательно начала темы, там только 2 типа, CData и CData*
отделить одно от другого можно через std::is_pointer
так же можно сравнить с нужным типом через std::is_same


Название: Re: Итераторы
Отправлено: Igors от Январь 29, 2021, 16:20
Касательно начала темы, там только 2 типа, CData и CData*
отделить одно от другого можно через std::is_pointer
так же можно сравнить с нужным типом через std::is_same
Тогда зачем было заявлять общность (темплейт) если латаем? Залатать и так можно. Да и "отделить" с помощью std::is_pointer (или std::is_same) не так уж легко, просто "if" не получится, нужно городить что-то типа std::enable_if  (помню смутно). Подозреваю что латка получится никак не меньше чем бесхитростная выше.

Повторюсь, дело не в том что, мол, не знаю как решать, "памагите" :) Но хочется сделать вккуратно, а не развешивать сопли.


Название: Re: Итераторы
Отправлено: Old от Январь 29, 2021, 16:26
Но хочется сделать вккуратно, а не развешивать сопли.
С теми требованиями из первого поста "вккуратно" не получится. Продолжайте развешивать сопли.

Это прекрасное решение и всем, кто в дальнейшем будет читать этот код, все сразу будет понятно. :)
Код
C++ (Qt)
void CBigClass::SomeMethod( bool useSelection )
{
 ..
 int limit = useSelection ? mSelection.size() : mData.size();
 auto dataIt = mData.begin();  
 auto selIt = mSelection.begin();  
 for (int i = 0; i < limit; ++i) {
   CData * dataPtr;
   if (useSelection) {
      dataPtr = *selIt;
      ++selIt;
   }
   else {
      dataPtr = &(*dataIt);
      ++dataIt;
   }
 
  ...
 }
}


Название: Re: Итераторы
Отправлено: RedDog от Январь 29, 2021, 17:34
https://en.cppreference.com/w/cpp/types/add_pointer
Опять не?


Название: Re: Итераторы
Отправлено: Igors от Январь 30, 2021, 10:00
https://en.cppreference.com/w/cpp/types/add_pointer
Опять не?
Ну вот, сейчас начнется
Цитировать
Уже столько вариантов предложили, а ему все не так!
:)

Но давайте все-таки посмотрим "что предложили". Ведь вся эта хренотень (is_pointer, add_pointer и.т.п.) - вовсе не обычные ф-ции которыми можно спокойно воспользоваться. Как я понимаю, они позволяют сделать темплейты что будут корректно вызываться для указателя и нет, но их надо еще делать, а как? (навскидку я не напишу)

Во-вторых, как-то "не концептуально". Объявление template означает что можем подавать что угодно, во всяком случае - многое. В этом и кайф этого угребочного мета-программирования. А сами обеспечиваем лишь 2 варианта: ссылка и указатель. Потребуется напр по значениям мапы - и все. Да, конкретно для данной задачи нужны только 2 варианта, но если пердлагаем "общность" - то надо ее поддерживать


Название: Re: Итераторы
Отправлено: RedDog от Февраль 01, 2021, 09:20
Объявление template означает что можем подавать что угодно, во всяком случае - многое.
Нет template говорит о том, что обработка переданных аргументов будет одинакова для разных типов.
А внутренняя реализация обработки зависит от разработчика.
Чем сильнее различается АПИ аргументов, тем больше будет геморроя при реализации.


Название: Re: Итераторы
Отправлено: Igors от Февраль 01, 2021, 11:09
Может лучше нацелить темплейт(ы) не на итератор, а на *итератор, т.е. разыменованную ссылку
Код
C++ (Qt)
template<class T>
CData & DataRef( T & ref ) {  return ref; }
 
CData & DataRef( CData  *& refPtr ) { return *refPtr; }
Выглядит гораздо проще чем is_pointer и.т.п. (как то писать - хз, а покрывает лишь один случай)

Однако же... как переменчива мода... Раньше страсти кипели, там всякие вариадики, SFINAE (если я правильно помню это слово), traits и еще бог весть что. А сейчас - та вроде никому это и не интересно. Мда...


Название: Re: Итераторы
Отправлено: Авварон от Февраль 01, 2021, 17:14
можно просто перегрузить функцию для ссылки и указателя без шаблонов=)

Код:
void foo(Type &t) {...}
void foo(Type *t) {foo(*t); }


Название: Re: Итераторы
Отправлено: Igors от Февраль 03, 2021, 07:47
 
можно просто перегрузить функцию для ссылки и указателя без шаблонов=)

Код:
void foo(Type &t) {...}
void foo(Type *t) {foo(*t); }
??? Не уловил идею


Название: Re: Итераторы
Отправлено: Fregloin от Февраль 15, 2021, 13:08
Вам уже указали решение вашей пробелмы - std::advance


Название: Re: Итераторы
Отправлено: Igors от Февраль 15, 2021, 14:13
Вам уже указали решение вашей пробелмы - std::advance
"А можно пример?" :) Потому что не вижу каким боком здесь поможет advance

И что там за "плохо скрываемое раздражение"? :) Нет удобного решения - ну у меня его тоже нет.


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 15, 2021, 12:02
Навеяло:

Джун: так, сейчас по быстрому наговнокодим, так, ну скомпилилось, ну вроде не падает... иногда... фух, в продакшен.

Мидл: так, что эти джуны там наваяли, ой блин, говнокод, лапша, нет структуры и ПолётаМысли... Ну ща тут абстрактную шаблонную фабрику прикрутим, там визитор, тут обсервер... 95 из 100 тестов прошло, значит уже хорошо, эй, менеджер, готово!

Синьйор: так, блин, что за GOFнокод... Фабрики в лес, визиторы в лес, оставим пару классов, чтобы KISS и иже с ним... Ага, раз в 50 быстрее работает... готово :)

Тимлид: народ, давайте-ка код ревью по быстрому проведём, а то отдавать скоро.

Начальник разработки: так, что там в задаче сказано? А, найти площадь прямоугольника. Где там мой калькулятор...


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 19, 2021, 22:51
Цитировать
struct CBigClass {
...
 QList<CData> mData;
 QSet<CData *> mSelection;
...
};

Я бы сделал так:
Код:
struct CBigClass {
...
 QList<CData> mData;
 QSet<std::reference_wrapper<CData>> mSelection;
...
};

Теперь тип элемента в контейнерах, можно сказать, одинаковый.

Если QSet<CData *> mSelection; нужно оставить, то сделайте zero-cost адаптер, чтобы оперировать ссылками, а не указателями:
Код:
struct CBigClass {
...
QSet<std::reference_wrapper<CData>>& mSelectionAsRefs()
{
static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));
return reinterpret_cast<QSet<std::reference_wrapper<CData>>&>(mSelection);
}
};

Итак, std::reference_wrapper внутренне реализован как указатель, но имеет семантику ссылки. Собственно, она часто и используется с контейнерами.

Теперь адаптер: он не имеет накладных расходов (ссовсем), поскольку размер std::reference_wrapper == размеру указателя, и мы жестко реинтерпретируем. Но равенство размеров не гарантируется (не уверен), поэтому мы это проверяем в compile-time static_assert-ом.

И еще. В вашем QSet<CData *> mSelection; могут быть нулевые указатели? Предполагаю - нет. Если указатель не может быть нулевым - используйте ссылку (во всех таких случаях). Читаемость и строгость кода же повышаются. Хотя, в плане читаемости - спорно (std::reference_wrapper<> - много букв  :)).


Название: Re: Итераторы
Отправлено: Igors от Апрель 20, 2021, 11:00
Да, интересный приемчик с reference_wrapper, не знал, спасибо


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 20, 2021, 11:08
Не вопрос. Да и это делеко не последний писк моды - это c C++11.


Название: Re: Итераторы
Отправлено: Авварон от Апрель 20, 2021, 12:41

Код:
return reinterpret_cast<QSet<std::reference_wrapper<CData>>&>(mSelection);

Это UB


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 20, 2021, 14:24
Это не UB. В худшем (маловероятном) случае, это compile-time error. Потому что так перед грязным хаком (reinterpret_cast) идет
Код:
static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));
что делает хак чистым. Прпосто вдумайтесь в смысл условия ассерта.

Если уж подходить совсем параноидально, то в теории UB возможен, но такая реализация std::reference_wrapper лишена практического смысла. Например,  std::reference_wrapper хранит не исходный указатель, а смещенный на 5, например, или в форме инверсных битов. 


Название: Re: Итераторы
Отправлено: Old от Апрель 20, 2021, 14:45
Ну это все таки грязный хак. :)
А что если у QSet для указателей и нет разные специализации?


Название: Re: Итераторы
Отправлено: Авварон от Апрель 20, 2021, 15:04
Это UB, в общем случае нельзя делать reinterpret_cast между unrelated типами.

https://en.cppreference.com/w/cpp/language/reinterpret_cast

Цитировать
std::pair<int, int> and std::pair<const int, int> are not similar.

Компилятор предполагает что 2 ссылки/указателя на различные типы не могут ссылаться не одну область памяти (strict alias rule) и строит на этом оптимизации.
reinterpret_cast ломает это предположение - если компилятор тупенький и не оптимизирует, то это будет работать, если умный, то сломается. UB.


Название: Re: Итераторы
Отправлено: Авварон от Апрель 20, 2021, 15:09
Ну или более сложный пример, вы знаете что на вашей платформе sizeof(bool) == sizeof(int8_t) (ну или char).
Значит вы можете сделать
Код:
vector<bool> bools;
auto &chars = reinterpret_cast<vector<char>&>(bools);
?


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 20, 2021, 15:53
А что если у QSet для указателей и нет разные специализации?
Ах, таки вы меня подсекли! Про возможную специализацию внешнего типа я упустил. В данном случае специализации QSet нет, работать будет. Но в общем случае - да, это нужно учитывать.

Авварон,
Код:
vector<bool> bools;
auto &chars = reinterpret_cast<vector<char>&>(bools);
Как метко подметил Old, причина этого UB в специализации vector<bool>. Она хранит биты, а не байты.

По поводу strict alias rule и оптимизации (привет restrict poiners): А как вы соотнесете это с placement new(), например?


Название: Re: Итераторы
Отправлено: Авварон от Апрель 20, 2021, 16:03
Ах, таки вы меня подсекли! Про возможную специализацию внешнего типа я упустил. В данном случае специализации QSet нет, работать будет. Но в общем случае - да, это нужно учитывать.

Там внутри могут быть всякие traits которые для разных типов разные.
Но в любом случае, даже если битовое представление одинаковое, это UB.

По поводу strict alias rule и оптимизации (привет restrict poiners): А как вы соотнесете это с placement new(), например?

В чем проблема-то? Единственный валидный юзкейз reinterpret_cast между разными типами это каст к массиву байт (или void*) и обратно.
Компилятор знает что вон тот char buffer[] потенциально может алиаситься с вот этим указателем и сгенерит менее производительный код.
placement new как раз подпадает под это - буфер чаров может алиаситься (и делает это) с указателем на произвольный T.
А вот любые другие типы (например int* в double*) алиаситься не могут.


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 20, 2021, 22:35
Цитировать
Там внутри могут быть всякие traits которые для разных типов разные.
Но в любом случае, даже если битовое представление одинаковое, это UB.
Могут, но это лишено практического смысла. Для контейнера важен только размер элемента (точнее, скажем "семантический размер", как в случае с bool), а сам элемнт, будь хоть POD, хоть класс с VMT - в плане хранения - все равно. Если обратное, то это уже не контейнер, а контейнер + еще что-то (т.е. bad design).  

Цитировать
В чем проблема-то? Единственный валидный юзкейз reinterpret_cast между разными типами это каст к массиву байт (или void*) и обратно.
Компилятор знает что вон тот char buffer[] потенциально может алиаситься с вот этим указателем и сгенерит менее производительный код.
placement new как раз подпадает под это - буфер чаров может алиаситься (и делает это) с указателем на произвольный T.
А вот любые другие типы (например int* в double*) алиаситься не могут
Я к тому, что поинтер-алиасинг это не только про reinterpret_cast. Вот этот код дает UB (печатает -1 вместо ожидаемого 0) в обоих случаях (MinGW 8.3, O2):
Код:
#include <iostream>

int foo(float* f, int* i)
{
*i = -1;
*f = 0.0f;

return *i;
}

int main(int, char *[])
{
int i;
//i = foo(reinterpret_cast<float*>(&i), &i);  // 1
i = foo(new(&i) float, &i);                   // 2
std::cout << i << std::endl;
}

А теперь вернусь в контекст нашей задачи - слегка модифицирую код на манер std::reference_wrapper, для упрощения я использовал просто структуру:
Код:
#include <iostream>

#if 0  // 0 - OK, 1 - UB
typedef float F;
#else
struct F
{
int i;
F(float v = 0.0f) { reinterpret_cast<float&>(i) = v; }
};
#endif

int foo(F* f, int* i)
{
*i = -1;
*f = 0.0f;

return *i;
}

int main(int, char *[])
{
int i;
//i = foo(reinterpret_cast<F*>(&i), &i);  // 1
i = foo(new(&i) F(), &i);                   // 2
std::cout << i << std::endl;
}
При #if 0 UB больше нет в обоих случаях. #if 1 - это как в предыдущем случае с UB.

Почему больше нет UB? - сходу не найду - но еще вроде со всремен C++11 правил алиасинга если один из типов агрегатный и включает другой членом - то это не UB.

Итого:
Типы reference_wrapper<T> и Т* должны быть безопасны в плане reinerpret_cast, т.к. первый инкапсулирует второй как член, что следует из того, что sizeof(reference_wrapper<T>) == sizeof(Т*)т.е. reference_wrapper<T> никак не может быть реализован по-другому, кроме как инкапсулировать указатель и больше ничего.


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 20, 2021, 23:00
От-т! Опять я упустил внешний контейнер (QSet).

Авварон, по алиасингу вынужден с вами также согласиться. Друзья, спасибо!


Название: Re: Итераторы
Отправлено: Igors от Апрель 21, 2021, 10:12
Код:
	*i = -1;
*f = 0.0f;
Ну вообще-то компилятор имеет право выполнять эти операции в любом порядке, поэтому если адрес перекрывается, то рез-т не определен, тонкости ре-интерпретации здесь ни при чем.

Пример vector<bool> приводится так часто/неизменно, что скорее говорит о том что никаких других просто нет  :)

С placement new как-то не уловил, неплохо бы пожевать. Спасибо

От-т! Опять я упустил внешний контейнер (QSet).
Типа "а вот возможен такой контейнер что будет иметь какие-нить traits - и тогда заклинит"? Может и да, но лично меня это совершенно не волнует. Я не собираюсь лепить врапперы всегда и везде, речь идет о конкретном случае в котором он весьма кстати. А объявить любой reinterpret_cast UB/хаком никакого ума не требует. Заметьте что чем больше человек говорит о корректности - тем меньше решений предлагает. Такой подход не конструктивен.

Цитата: AkonResumed link=topic=32938.msg244690#msg244690
Авварон, по алиасингу вынужден с вами также согласиться. Друзья, спасибо!
Возможно/предполагаю, Вы почувствовали что разбор этих тонкостей (или толстостей) отнимает слишком много времени, а работа-то стоит. Да, согласен :)


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 10:21
Может и да, но лично меня это совершенно не волнует. Я не собираюсь лепить врапперы всегда и везде, речь идет о конкретном случае в котором он весьма кстати.
Да вы можете это брать и использовать. :)
Дальнейшее обсуждения больше для разработчиков, что бы никто из них по неосторожности у себя не задействовал.


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 21, 2021, 10:36
Цитировать
Вот этот код дает UB (печатает -1 вместо ожидаемого 0)

Почему UB? Он и должен выводить -1.
В коде же явно написано "*i = -1", а i передаётся поинтером 1 в 1.

Другое дело, что такой код в продакшене никто использовать не будет.
Разве что как тест для компилятора.




Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 21, 2021, 10:53
UB, потому что следом *f = 0.0f; зануляет туже самую область памяти. Так и происходит, но компилятор, при возврате в return *i; попросту не перезагружает значение из памяти, потому как считает, что f не может указывать на ту же ячеку, что и i, соответственно, она не может быть изменена через f. Для return *i; компилятор просто хардкодит 1 в eax/rax (регистр, через который идет возврат значения функции).


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 21, 2021, 11:02
Да, и порядок выполнения операций здесь не при чем. Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык. UB возникает именно из-за оптимизатора и формального правила стандарта, которое он использует для оптимизации.


Название: Re: Итераторы
Отправлено: Igors от Апрель 21, 2021, 11:23
Да, и порядок выполнения операций здесь не при чем. Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык. UB возникает именно из-за оптимизатора и формального правила стандарта, которое он использует для оптимизации.
Не уверен. Вот был интересный случай
Код
C++ (Qt)
float Rand( uint32 & seed );
 
QVector3D vec(Rand(seed), Rand(seed), Rand(seed));
Эта ошибка долго оставалась незамеченной, но когда перешел на шланг - тот выдал варнинт, молодец.

Конечно в в любом случае перекрывать адреса = ненужный поиск приключений. Вообще употребляемые здесь слова "продакшн", "разработка" и.т.п. - это просто "надувание щек", здесь обсуждается всего лишь мелкая деталь  :)


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 11:28
Вообще употребляемые здесь слова "продакшн", "разработка" и.т.п. - это просто "надувание щек", здесь обсуждается всего лишь мелкая деталь  :)
Боже, от вас слышать про надувание щек... :)


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 21, 2021, 11:29
И как вы предполагали - какой Rand будет вызван первым - левый или правый?


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 21, 2021, 11:41
В этом контексте (порядка вычисления аргументов) частая ошибка, допускающая лик:
Код:
void foo(std::shared_ptr<MyClass> c, int x);

foo(std::shared_ptr<MyClass>(new MyClass), getXValue());



Название: Re: Итераторы
Отправлено: Авварон от Апрель 21, 2021, 11:47
Вот этот код дает UB (печатает -1 вместо ожидаемого 0) в обоих случаях (MinGW 8.3, O2):

Да, прикольно, не задумывался о том что placement new можно звать на unrelated типах.
Небось тоже где-то прописано что так делать нельзя (кроме char*/void*)


Название: Re: Итераторы
Отправлено: Авварон от Апрель 21, 2021, 11:52
И как вы предполагали - какой Rand будет вызван первым - левый или правый?

В 2ГИСе мы спрашивали похожую задачку
std::cout << f() << f() << f() << std::endl;

где f() имеет сайд-эффект (делает i++)

никто не решил (я в том числе)


Название: Re: Итераторы
Отправлено: Igors от Апрель 21, 2021, 11:58
И как вы предполагали - какой Rand будет вызван первым - левый или правый?
Мне все равно (будет надо - посмотрю в отладчике), главное рез-т должен быть воспроизводимым при том же seed.

Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык.
Это противоречит духу/идее "конвейера". Во всяком случае "распаковкой операндов" для последующих команд(ы) процессор занимается.

никто не решил (я в том числе)
Любая задача имеет по меньшей мере одно решение: снятие самой задачи/проблемы. Здесь оно и будет лучшим - не нужны такие заморочки.


Название: Re: Итераторы
Отправлено: Авварон от Апрель 21, 2021, 12:03
Любая задача имеет по меньшей мере одно решение: снятие самой задачи/проблемы. Здесь оно и будет лучшим - не нужны такие заморочки.

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


Название: Re: Итераторы
Отправлено: Igors от Апрель 21, 2021, 12:17
Ну там задачи были разной сложности что позволяло оценить уровень кандидата. Полезно иметь задачу с подвохом ультимативной сложности. Если человек скажет какой-то один из возможных вариантов, то норм (там еще надо продраться сквозь код же), а если про точки следования вспомнит - ваще бох. Правда на моей памяти никто без подсказок не вспомнил, но это и не особо влияло.
Заинтриговали :) По-моему без затей, слева направо, зачем прилагать усилия хранить то что еще нельзя слить.

К сожалению, тесты никак не оценивают инициативность, способность находить решения и многое другое, тупенькое "знание справочника" обычно рулит :'(


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 13:31
В 2ГИСе мы спрашивали похожую задачку
std::cout << f() << f() << f() << std::endl;

где f() имеет сайд-эффект (делает i++)

никто не решил (я в том числе)
Нужно было вспомнить, что эта запись сахар для такой :)
Код
C++ (Qt)
std::cout.operator<<( f() ).operator<<( f() ).operator<<( f() ).operator<<( std::endl );
 
А они всегда выполняются слева направо, т.к. здесь следующий оператор всегда применяется к результату предыдущего.

А вот с boo( f(), f(), f() ), не все так однозначно и, если мне не изменяет память, это отдается на откуп компилятора. Как правило f() будет вызываться справа налево, по крайней мере я не знаю ни одного компилятора, у которого это не так.


Название: Re: Итераторы
Отправлено: Авварон от Апрель 21, 2021, 15:15

Нужно было вспомнить, что эта запись сахар для такой :)
Код
C++ (Qt)
std::cout.operator<<( f() ).operator<<( f() ).operator<<( f() ).operator<<( std::endl );
 


Да, но нет, это не члены класса а свободные функции:

Код
C++ (Qt)
operator<<(operator<<(operator<<(operator<<( std::cout, f() ), f() ),  f() ), std::endl );
 
и что выполнится вначале - operator<<( std::cout, f() ) или f() - бабка надвое сказала


Название: Re: Итераторы
Отправлено: Igors от Апрель 21, 2021, 15:22
Вспомнилось что когда-то весьма усердно изучалось нечто вроде
Цитировать
Pascal передает параметры слева направо, стек очищает сама ф-ция. А вот С - справа налево и стек очищает вызывающий. Поэтому можно передавать переменное число параметров и.т.д и.т.п. (потом еще для калбэков надо было учитывать)
Ну может какой-то смысл в этом и есть, но в принципе это совершенно бесполезное забивание головы всякой фигней  :)


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 15:22
Да, но нет, это не члены класса а свободные функции:
А у какого компилятора operator<< это свободная функция?
У gcc и clang это члены класса. Даже у msvc это член класса.

и что выполнится вначале - operator<<( std::cout, f() ) или f() - бабка надвое сказала
Что вначале выполниться boo или f? :)
Код
C++ (Qt)
boo( f() );
 

а здесь:
Код
C++ (Qt)
auto &os1 = std::cout.operator<<( f() );
auto &os2 = os1.operator<<( f() );
auto &os3 = os2.operator<<( f() );
auto &os4 = os3.operator<<( std::endl );
 

Даже в вашем варианте порядок вызовов четко определен. :)


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 15:23
Вспомнилось что когда-то весьма усердно изучалось нечто вроде
Цитировать
Pascal передает параметры слева направо, стек очищает сама ф-ция. А вот С - справа налево и стек очищает вызывающий. Поэтому можно передавать переменное число параметров и.т.д и.т.п. (потом еще для калбэков надо было учитывать)
Ну может какой-то смысл в этом и есть, но в принципе это совершенно бесполезное забивание головы всякой фигней  :)
Да-да, продолжайте наблюдение. :)


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 21, 2021, 16:33
Кстати, при прочих равных (когда нет выделения общих вычислений на аргументах) на практике соглашение о вызове по идее должно коррелировать с порядком вычисления аргументов - тот, который пушим первым первым и вычисляем, но не вычисляем первым аргумент из середины, т.к. его нельзя сразу запушить. Но, например, при register calling convension то, что будет передаваться в регистрах можно вычислять в любом порядке. Так что вариаций много.


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 16:37
Кстати, при прочих равных (когда нет выделения общих вычислений на аргументах) на практике соглашение о вызове по идее должно коррелировать с порядком вычисления аргументов - тот, который пушим первым первым и вычисляем, но не вычисляем первым аргумент из середины, т.к. его нельзя сразу запушить. Но, например, при register calling convension то, что будет передаваться в регистрах можно вычислять в любом порядке. Так что вариаций много.
Как я помню, это отдается на откуп компилятора. Поэтому может быть что угодно, закладываться на это нельзя.


Название: Re: Итераторы
Отправлено: Авварон от Апрель 21, 2021, 17:00
Даже в вашем варианте порядок вызовов четко определен. :)

Ну вот gcc 4.6 печатает "210", а 10.3 печатает "120" https://godbolt.org/z/Pc9fa4bf9
Там в целом были какие-то изменения в с++11, но яхз, поправлено это или "везет" что все новые компиляторы печатают одинаково.

Что вначале выполниться boo или f? Улыбающийся

вот только оператор (даже член класса) это функция от двух аргументов
Код:
foo(f(), g());
не понимаю почему функция-член класса гарантирует что "this" вычисляется первым. где это написано?


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 21, 2021, 17:08
И как вы предполагали - какой Rand будет вызван первым - левый или правый?

В 2ГИСе мы спрашивали похожую задачку
std::cout << f() << f() << f() << std::endl;

где f() имеет сайд-эффект (делает i++)

никто не решил (я в том числе)


И в чём проблема? Это пайплайн. Он должен выполняться строго слева направо (ибо в этом его суть) и никак иначе. Сайд-эффект в чём?


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 17:09
Ну вот gcc 4.6 печатает "210", а 10.3 печатает "120" https://godbolt.org/z/Pc9fa4bf9
Вот два скрина, в обоих случаях вроде 012?
Увидел где 210.


вот только оператор (даже член класса) это функция от двух аргументов
Код:
foo(f(), g());
не понимаю почему функция-член класса гарантирует что "this" вычисляется первым. где это написано?
Ну и что, что два аргумента. В этом операторе нам не важно, какая функция будет выполнена первой operator<<( this(), f() )


Название: Re: Итераторы
Отправлено: Авварон от Апрель 21, 2021, 17:11
у вас в обоих случаях 10.3 (см правый pane)


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 17:22
Да, жесть. Нужно быть осторожным. :)

За такое, конечно нужно яйца откручивать gcc-шникам. :)
Спасибо хоть gcc с 7.1 начал нормальный код генерить.


Название: Re: Итераторы
Отправлено: Old от Апрель 21, 2021, 17:56
И в чём проблема? Это пайплайн. Он должен выполняться строго слева направо (ибо в этом его суть) и никак иначе. Сайд-эффект в чём?
В том, что gcc до 7.1 лажал и генерировал шляпу. :)


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 21, 2021, 23:30
И в чём проблема? Это пайплайн. Он должен выполняться строго слева направо (ибо в этом его суть) и никак иначе. Сайд-эффект в чём?
В том, что gcc до 7.1 лажал и генерировал шляпу. :)

А. Тогда стыдно им всем. Должно бы быть...


Название: Re: Итераторы
Отправлено: Old от Апрель 22, 2021, 07:02
Там в целом были какие-то изменения в с++11, но яхз, поправлено это или "везет" что все новые компиляторы печатают одинаково.
Конечно поправили, gcc до 7 генерировал неправильный код.
В случае с стримами порядок вызовов функций четко определен.

Код
C++ (Qt)
std::cout << a() << b() << c() << std::endl;
 

Если в b() генерируется исключение, то в потоке должен остаться результат от a(). А теперь посмотрите что кодогенерит gcc до 7. :)


Название: Re: Итераторы
Отправлено: Igors от Апрель 22, 2021, 07:19
Надеюсь "волна" прошла и можно поговорить о другом. Для удобства повторю часть стартового поста
Есть большой класс, и в нем
Код
C++ (Qt)
struct CBigClass {
...
QList<CData> mData;
QSet<CData *> mSelection;
...
};
А насколько вообще хорош (правомерен и.т.п.) такой выбор контейнеров?

Увы, подозреваю что словоохотливость сразу упадет до нуля. Ну конечно "надо знать задачу" (дежурная отмазка). Да задача самая банальная и которая есть у всех. Есть объекты, они рисуются в окнах и их список предъявляется юзеру (QTreeWidget), конечно их можно выбирать, добавлять, удалять, редактировать. CData - обычный себе объект, не маленький но и не большой.

Единственная специфика - их может быть неприятно много (порядка 100К и больше), поэтому простецкий selectItem не катит в UI. 

So ?


Название: Re: Итераторы
Отправлено: Old от Апрель 22, 2021, 07:28
А насколько вообще хорош (правомерен и.т.п.) такой выбор контейнеров?
Он великолепен. :)

Основная ваша проблема в этом:
(как и делать из тела метод)
Как только вы сделаете отдельный метод, все у вас начнет получаться. :)


Название: Re: Итераторы
Отправлено: Авварон от Апрель 22, 2021, 10:55
Если в b() генерируется исключение, то в потоке должен остаться результат от a(). А теперь посмотрите что кодогенерит gcc до 7. :)

Ну да, но это с с++11, на тот момент задача была валидная. Я таки носили простыню про sequencing, пункт 19 https://en.cppreference.com/w/cpp/language/eval_order
Но надо понимать что это поправили только для operator<< и >>, для operator+ порядок по-прежнему не задан.


Название: Re: Итераторы
Отправлено: Авварон от Апрель 22, 2021, 11:02

So ?

Код:
void doWork(const CData &data)
{
}

template<typename С>
void iterateContainer(const С& c)
{
    for (const auto &item: c)
    {
        if constexpr (std::is_pointer_v<C::value_type>)
            doWork(*item);
        else
            doWork(item);
    }
}


Название: Re: Итераторы
Отправлено: Igors от Апрель 22, 2021, 11:21
Код:
..
}
Про выделение метода сказано в стартовом посте - это не всегда удобно. Но сейчас речь уже не об этом, я спрашиваю удачно ли я выбрал контейнеры вообще (см предыдущий пост). Как буржуи гоаорят
Цитировать
Can you read, or only write?
:)


Название: Re: Итераторы
Отправлено: Авварон от Апрель 22, 2021, 11:26
с 50% вероятностью ответ нет


Название: Re: Итераторы
Отправлено: Igors от Апрель 23, 2021, 08:44
с 50% вероятностью ответ нет
Не думаю здесь есть какие-то "варианты зависящие от задачи". Напр "в вдруг требуется пулеметная вставка/удаление?". Да, так бывает, но вряд ли такой контейнер дается юзверю в UI. А так надо лишь позаботиться чтобы multiply вставки/удаления выполнялись за один проход.

Или популярная байка "std::vector лучше"  (чем QList). В данном случае не вижу чем. "Нормальные" объекты в (подавляющем) большинстве случаев должны быть неперемещаемы. Замена QList<CData> на std::vector<CData *> дает немного больше возможностей, но заметно напрягает с удалением

Рекомендация выше юзать ссылку (вместо указателя). Обычно я охотно это делаю, но в данном случае это "создает впечатление владения", а selection ну вот никак не владеет. Некоторый минус - QSet жрет многовато памяти при обильном selection.

Что же нового/современного можно использовать? (вместо структур > 10 летней давности). Конечно "с выгодой", а не так, абы накрутить. Выходит что здесь и ничего. Или может задача "ну совсем уж серая, банальная", для нее и старого хватит? Или как?


Название: Re: Итераторы
Отправлено: Old от Апрель 23, 2021, 09:18
Что же нового/современного можно использовать? (вместо структур > 10 летней давности).
Для того, что бы появились новые структуры данных, эти новые структуры сначала должен кто-то придумать.
Т.к. от std-шников ждать ничего не приходится, то остаетесь только вы. :)
А как придумаете, то std-шники сразу эти новые структуры реализуют в своем убогом std. :)


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 23, 2021, 11:20
Что же нового/современного можно использовать? (вместо структур > 10 летней давности). Конечно "с выгодой", а не так, абы накрутить. Выходит что здесь и ничего. Или может задача "ну совсем уж серая, банальная", для нее и старого хватит? Или как?

Вот вообще не понимаю проблемы, если честно.
Есть два вида массивов: со ссылками и с указателями.
Элементы обрабатываются одинаково.
Вывод: обработчик элементов должен быть общий, циклы - разнесены.

for (auto& item: mData)
  processItem(item);

и

for (auto* item: mSelection)
  processItem(*item);

И всю обработку столкать в void processItem(CData& item).
Если надо, processItem к тому же можно сделать виртуальной, чтобы в возможных наследниках можно было бы (теоретичски) менять поведение.


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 23, 2021, 11:26
Цитировать
Рекомендация выше юзать ссылку (вместо указателя). Обычно я охотно это делаю, но в данном случае это "создает впечатление владения", а selection ну вот никак не владеет. Некоторый минус - QSet жрет многовато памяти при обильном selection.
В этом правда есть.

Изложу лишь следущее: если бы selection был владеющий отдельной копией объекта, т.е. QSet<CData> (не CData*) - это как-то принципиально сломало бы ваш код? Предположу, что нет. Указатель вы используете для уменьшения потребления памяти, ну и бонусом хэш-функция уже есть. Т.е. использование в selection указателей или самих данных определяется их размером, что, согласитесь, семантически ничтожный аргумент. Поэтому ссылка в данном случае - это эффективное владение.

Я допускаю, что выбор указателя может быть как-то связан с хэшированием, например CData неуникальны. Или для них нетривиально придумать хорушую хэш функцию. 

Ваш код был бы проще, если бы было единообразие, которое позволило бы единую обработку шаблонной функцией без is_pointer:
Код:
struct CBigClass {
...
 QList<CData*> mData;
 QSet<CData *> mSelection;
...
}

или

struct CBigClass {
...
 QList<CData> mData;
 QSet<reference_wrapper<CData>> mSelection;
...
};

Порядок таков, что если CData - это класс-сущность (identity class), то используются указатели, если класс данных - то сами классы.

Цитировать
Не думаю здесь есть какие-то "варианты зависящие от задачи"
От специфики задачи зависит, порой, все  :) Если у вас часто случаются пулеметные вставки, может рассмотреть вариант сперва сформировать вставку в виде вектора, а потом вставить ее в контейнер как единое целой, т.е. никаких multiply вставок?

Цитировать
Или популярная байка "std::vector лучше"  (чем QList).
Ну как байка? Например, в QList не засунешь std::unique_ptr, а std::unique_ptr в контейнере - это сплошь и рядом. Да, вы можете засунуть std::shared_ptr/QSharedPointer - но это другая (более широкая) семантика, да и код будет менее эффективным.

Например, QList нет еmplace, а еmplace, в свою очередь, может оказаться решающим доводом в пользу типа данных контейнера, соот-но QList дает меньшую свободу.


Название: Re: Итераторы
Отправлено: Igors от Апрель 23, 2021, 15:05
И всю обработку столкать в void processItem(CData& item).
Такой processItem может потребовать пяток-десяток доп аргументов что вычисляются в caller'e до цикла

Вот вообще не понимаю проблемы, если честно.
Нет какой-то "большой проблемы", просто хочется писать удобнее/приятнее, это нормально. Напр хорошо бы так
Код
C++ (Qt)
auto & dst = useSelection ? mSelection : mData;
for (auto & data : dst) {
..
}
 
А вместо этого тело выделяй, темплейты заряжай, еще и "сбоку бантик" в виде is_pointer. Где же хваленая гибкость итераторов?

Ваш код был бы проще, если бы было единообразие, которое позволило бы единую обработку шаблонной функцией без is_pointer:
Конечно, проблему стартового поста это решает. Но какой ценой, не получу ли др минусы? Напр первый вариант (оба эл-та указатели) перекладывает работу (во всяком случае часть) на внимательность программиста - в одном контейнере erase должен сопровождаться удалением, в др нет. С враппером выглядит заманчиво, но пока не пробовал, не знаю что там может полезет.

Цитата: AkonResumed link=topic=32938.msg244746#msg244746
Например, в QList не засунешь std::unique_ptr ..
...
Например, QList нет еmplace ...
Да, есть новые вкусные плюшки  :) Но нужны ли они для данных стартового поста? Это стандартная задача "предяъвить список объектов", не ошибусь если скажу что ее делал каждый присутствующий. Что станет лучше от использования "новых" средств и каких ?


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 23, 2021, 15:09
А что мешает тогда создать временный вектор поинтеров на mData? Это всего-то 2 строчки кода..

QList<CData*> dataList;
for (auto& data: mData) dataList << &data;

ну и дальше где нужно выбирать:

auto & dst = useSelection ? mSelection : dataList;
for (auto & data : dst) {
 ..
}


Название: Re: Итераторы
Отправлено: Igors от Апрель 23, 2021, 15:11
..что мешает тогда создать временный вектор ..
Угрызения совести :) Все-таки это говнокод


Название: Re: Итераторы
Отправлено: Old от Апрель 23, 2021, 15:21
Угрызения совести :) Все-таки это говнокод

Я на это смотрю и думаю: Да, этому парню говнокод не предложишь.

Код
C++ (Qt)
void CBigClass::SomeMethod( bool useSelection )
{
 ..
 int limit = useSelection ? mSelection.size() : mData.size();
 auto dataIt = mData.begin();  
 auto selIt = mSelection.begin();  
 for (int i = 0; i < limit; ++i) {
   CData * dataPtr;
   if (useSelection) {
      dataPtr = *selIt;
      ++selIt;
   }
   else {
      dataPtr = &(*dataIt);
      ++dataIt;
   }
 
  ...
 }
}
 


Название: Re: Итераторы
Отправлено: Racheengel от Апрель 23, 2021, 17:40
..что мешает тогда создать временный вектор ..
Угрызения совести :) Все-таки это говнокод

Ну, это скажем так, простое решение малой кровью (без лишних классов-итераторов-шаблонов).

Но лучшим решением было бы void processItem(CData& item).


Название: Re: Итераторы
Отправлено: Igors от Апрель 24, 2021, 06:45
Да, совсем забыл привести свое решение. Начнем с этого
Код
C++ (Qt)
void CBigClass::SomeMethod( bool useSelection )
{
 ..
 int limit = useSelection ? mSelection.size() : mData.size();
 auto dataIt = mData.begin();  
 auto selIt = mSelection.begin();  
 for (int i = 0; i < limit; ++i) {
   CData * dataPtr;
   if (useSelection) {
      dataPtr = *selIt;
      ++selIt;
   }
   else {
      dataPtr = &(*dataIt);
      ++dataIt;
   }
 
  ...
 }
}
 
Все тут вполне хорошо, пусть десяток лишних строк, от этого не умирают. Чтобы схлопнуть надо попрятать итераторные кишки в класс
Код:
void CBigClass::SomeMethod( bool useSelection ) 
{
  ..
  CDataIt it(*this, useSelection);
  while (it.HasNext()) {
    CData & data = it.GetNext();
    ....
}
И пусть Has/GetNext разбираются какой итератор продвигать и как вернуть ссылку. Правда как оформить в стиле плюсов (for auto & data : it) я не знаю :)


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 24, 2021, 12:02
С вашего позволения вернусь к обозначенной проблеме UB reinterpret_cast'a. Изначальный вариант:
Код:
QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() 
{
static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));
return reinterpret_cast<QSet<std::reference_wrapper<CData>>&>(mSelection);
}
полагаю, можно сделать полностью безопасным вот так:
Код:
QSet<std::reference_wrapper<CData>>& mSelectionAsRefs()
{
static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));

QSet<CData*>* src = &mSelection;
QSet<std::reference_wrapper<CData>>* alias;

std::memcpy(&alias, &src, sizeof(alias));  // starts lifetime of alias as QSet<CData*>*
return *alias;
}
Фишка этого приема в том, что memcpy() дает понять компилятору, что alias стартует как тип src, т.е. QSet<CData*>*.

Или эквивалент (м.б. более понятный):
Код:
QSet<std::reference_wrapper<CData>>& mSelectionAsRefs()
{
static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));

QSet<CData*>* src = &mSelection;
void* alias;

std::memcpy(&alias, &src, sizeof(alias));  // starts lifetime of alias as QSet<CData*>*
return *static_cast<QSet<std::reference_wrapper<CData>>*>(alias);
}
Здесь alias прямо декларируется как void* - тип, полностью совместимый с любым указателем (альтернативно, можно char* alias[sizeof(src)]), и далее используется static_cast.

Естественно, после оптимизации новый вариант относительно старого не должен иметь никаких дополнителных расходов.

Остается только одна потенциальная проблема - возможная специализация контейнера для указателей. В принципе, ее легко решить, если сделать свою специализацию контейнера для std::reference_wrapper<CData>, которая будет иметь битовую идентичность с контейнером CData*. Но это лениво (много букв).


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 24, 2021, 14:28
Да, и в С++20 это делается уже так (std::bit_cast):
Код:
QSet<std::reference_wrapper<CData>>& mSelectionAsRefs() 
{
static_assert(sizeof(CData*) == sizeof(std::reference_wrapper<CData>));
return std::bit_cast<QSet<std::reference_wrapper<CData>>&>(mSelection);
}
Т.е. дали заднюю со своим strict type aliasing.

Еще вариант (ИМХО, в общем случае малоприемлемый) - выключить strict type aliasing опцией компилятора. Ядро Линукса, например, компилится с -fno-strict-aliasing, а там, как можно догадаться, еще те любители реинтерпретации.


Название: Re: Итераторы
Отправлено: Igors от Апрель 24, 2021, 14:52
Фишка этого приема в том, что memcpy() дает понять компилятору, что alias стартует как тип src, т.е. QSet<CData*>*.
По-моему memcpy просто "соответствует своему названию" - и все. Каким образом он может что-то "дать понять" ???. Также что значит "стартует как"   ???

Ну размеры эл-тов равны - и все, приводим, не понял чего Вы еще опасаетесь? Мифических хвостов? В общем - поясните


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 24, 2021, 15:46
Цитировать
"стартует как"
Код:
void* p;          // declaration
p = new int(5);   // p begins its lifetime with dynamic type int*

Вот здесь по сути вопроса изложено четко и с примером: https://en.cppreference.com/w/cpp/string/byte/memcpy


Название: Re: Итераторы
Отправлено: Igors от Апрель 24, 2021, 16:58
Код:
void* p;          // declaration
p = new int(5);   // p begins its lifetime with dynamic type int*

Вот здесь по сути вопроса изложено четко и с примером: https://en.cppreference.com/w/cpp/string/byte/memcpy
Прочел дважды, ничего нового не почерпнул :) Про "lifetime" там ничего нет. Да и откуда оно возьмется для объекта созданного в куче, сам распределил - сам и удаляй


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 24, 2021, 21:57
Те же яйца, только в профиль:
Код:
int i;
void* p;  // declaration
p = &i;  // p begins its lifetime with dynamic type int*

memcpy: Notes

std::memcpy may be used to **implicitly create** objects in the destination buffer.
...
Where strict aliasing prohibits examining the same memory as values of two different types, std::memcpy may be used to convert the values.

И далее почитать по ссылке https://en.cppreference.com/w/cpp/language/object#Object_creation что такое объекты и как они создаются.


Название: Re: Итераторы
Отправлено: Igors от Апрель 25, 2021, 08:19
Те же яйца, только в профиль:
Код:
int i;
void* p;  // declaration
p = &i;  // p begins its lifetime with dynamic type int*
И что? Ну присвоили void * адрес чего-то, на здоровье. Но никаких "вытекающих" из этого нет, поведение p будет точно таким же как и при любой другой его установке. Неявное создание с помощью memcpy - ну да, если объект "тривиально копируемый" (как сейчас говорят), то можно и так, хотя времена таких трюков давно прошли, ну разве в контейнере чтобы пошустрее.

И тот reinterpret_cast, нафиг он нужен? Тем более memcpy. Да просто так
Код
C++ (Qt)
using CDataRef = std::reference_wrapper<CData>;
QSet<CDataRef>& SelectionAsRefs( void )
{
static_assert(sizeof(CData*) == sizeof(CDataRef));
return *(QSet<CDataRef> *) &mSelection;
}
 
И вся любовь.


Название: Re: Итераторы
Отправлено: Old от Апрель 25, 2021, 09:02
И тот reinterpret_cast, нафиг он нужен? Тем более memcpy. Да просто так
Код
C++ (Qt)
using CDataRef = std::reference_wrapper<CData>;
QSet<CDataRef>& SelectionAsRefs( void )
{
static_assert(sizeof(CData*) == sizeof(CDataRef));
return *(QSet<CDataRef> *) &mSelection;
}
 
И вся любовь.
Точно.
А придурки из комитета все какие то касты придумывают, то static_cast, то reinterpret_cast, сейчас bit_cast придумали. Нечем им заняться. :)
Зачем? Если можно сишным кастануть и вся любовь. :)


Название: Re: Итераторы
Отправлено: AkonResumed от Апрель 25, 2021, 09:59
Да, любовь - странная штука:
Код:
#include <QSet>
#include <iostream>

using namespace std;

typedef int CData;
using CDataRef = reference_wrapper<CData>;

QSet<CData*>* mSelection = new QSet<CData*>;

QSet<CDataRef>*& SelectionAsRefs( void )
{
static_assert(sizeof(CData*) == sizeof(CDataRef));
return *(QSet<CDataRef> **) &mSelection;
}

static QSet<CData*>* makeUB(QSet<CData*>** src, QSet<CDataRef>** alias)
{
static QSet<CData*> data = { new CData };  // set with one element

*src = &data;
*alias = nullptr;

return *src;  // what will be there: &data or nullptr?
}

int main(int, char *[])
{
mSelection = makeUB(&mSelection, &SelectionAsRefs());
std::cout << (!mSelection ? "OK" : "UB") << std::endl;
}
Этот код дает UB при -O2. Я вынужден был перейти от QSet<CData*> к QSet<CData*>*. Непосредственно тип QSet<CData*> UB не дает. Полагаю, что давал бы, если бы в нем был "простой" конструктор.


Название: Re: Итераторы
Отправлено: Igors от Апрель 25, 2021, 10:41
Этот код дает UB при -O2.
С перекрытием полей много чего "дают" (правда никто не хочет "получать"). Как бы Вы ни крутили, смысл один - вот эта "ячейка памяти" должна трактоваться как "нужный тип", без всяких изменений. Сишный каст это делает (в крайнем случае через void *) - ну и слава богу.

Разве Вы не чувствуете что "порог сложности" давно уже пройден? Не нужно решение такой ценой, даже если оно 100% корректно.

И вообще, чего Вы уперлись в эту мелочь? Вот сегодня создал тоже интересную темку. А какая шикарная тема про интеграцию скриптов. Был бы умный человек, а о чем с ним поговорить - я найду, не переживайте  :)


Название: Re: Итераторы
Отправлено: Old от Апрель 25, 2021, 11:05
Разве Вы не чувствуете что "порог сложности" давно уже пройден? Не нужно решение такой ценой, даже если оно 100% корректно.
Использование итераторов полностью разрывает порог сложности. И цена решения непомерна. :)

Был бы умный человек, а о чем с ним поговорить - я найду, не переживайте  :)
Потому что на целом форуме желающих почти не осталось. :)