Russian Qt Forum
Ноябрь 25, 2024, 05:19 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 3 [4] 5   Вниз
  Печать  
Автор Тема: Ненулевые указатели  (Прочитано 34024 раз)
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #45 : Декабрь 03, 2019, 14:28 »

То есть вы часто пишете код вида?
Код:
son.setParents(&parent{"Stepanida"}, &parent{"Stepan"});
с чего ты взял своё "то есть" ?

Оно вообще скомпилируется?


https://rextester.com/KOASHD96152
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #46 : Декабрь 03, 2019, 14:33 »

Это ж не сидеть неделями в отладчике
Вот он признак "профессионализма". Улыбающийся
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #47 : Декабрь 03, 2019, 15:13 »

ты понимаешь, что твоё "то, что можно выявить в compile-time" - это менее 1% всех реальных кейсов использования?
в то время как на долю рантайма придётся больше 99% ?

Это вы такое процентное соотношение придумали, зачем мне его понимать? Я считаю, что нормальный компилятор побольше может. По крайней избавит от получения висячего указателя на ровном месте.

Можно было ответить короче: уповать.
не распарсил эту фразу.

Сложности с отслеживанием нити разговора?
Что тогда предлагаете делать? Уповать на то, что другие догадаются, что автор задумал поле непустым, хотя там обычный указатель? Или комментарий для поля писать: "The m_parent must be not_null."? Улыбающийся

немножко неочевидна такая твоя реакция.
ты сам то смотрел?

или может ты просто не догоняешь?

в обоих случаях будет взят указатель или ссылка на временный объект.
в обоих случаях ты получишь UB

указатель:
https://rextester.com/KOASHD96152

Мда... не зря я недолюбливаю майкрософтовский компилятор  Веселый. Попробуйте собрать этот код в gcc или clang.

просто код нужно писать грамотно:
https://rextester.com/LLDG51891

Ага, каждый раз писать лабуду типа "static_assert(!::std::is_rvalue_reference<parent&&>::value, "temporary objects prohibited"); это "просто" Улыбающийся.

По публичному интерфейсу:
Код
C++ (Qt)
struct child
{
   template<class parent>
   child(parent&& mother, parent&& father) noexcept;
 
   template<class parent>
   void setParents(parent&& mother, parent&& father) noexcept;
 
   ...
};

пользователи класса подумают, что могут передавать объекты в любом виде (по значению, по lvalue ссылке, по rvalue ссылке), а на деле окажется что не в любом. Откуда они узнают, в каком виде нельзя передавать? Наугад перебирать разные типы и смотреть, скомпилируется или нет? Или придётся лезть в исходники этого "грамотного" кода и разбирать его? Очень "понятно и логично".

вот ты в качестве примера привел проблему,
которая с твоим not_null вообще никак не коррелирует.

Напрямую коррелирует. Вариант с gsl::not_null вы, похоже, так и не пробовали. Даже интересно, как майкрософтовский компилятор там себя поведёт.


Как предлагаете переписать такую абстрактную фабрику, как например QItemEditorFactory, чтобы она возвращала объекты по значению? Тогда может и найдёте одну из причин существования юника.

нафига?Непонимающий
это - не твоя фабрика.
зачем её переписывать?

Чтобы долго не объяснять, что такое "абстрактная фабрика". Поэтому и написал "такую абстрактную фабрику, как например QItemEditorFactory". Не именно её, а "такую как", чтобы был наглядный пример перед глазами. Не думал, что с пониманием этого предложения могут возникнуть сложности.
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #48 : Декабрь 03, 2019, 15:43 »

То есть вы часто пишете код вида?
Код:
son.setParents(&parent{"Stepanida"}, &parent{"Stepan"});
с чего ты взял своё "то есть" ?

Оно вообще скомпилируется?

https://rextester.com/KOASHD96152

Те не менее, вопрос актуальный. Вы часто берёте адрес временного объекта?
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #49 : Декабрь 03, 2019, 15:51 »

пользователи класса подумают, что могут передавать объекты в любом виде (по значению, по lvalue ссылке, по rvalue ссылке), а на деле окажется что не в любом. Откуда они узнают, в каком виде нельзя передавать? Наугад перебирать разные типы и смотреть, скомпилируется или нет? Или придётся лезть в исходники этого "грамотного" кода и разбирать его? Очень "понятно и логично".

Код, блин, у него, грамотный....

https://rextester.com/ISZFW96211
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #50 : Декабрь 03, 2019, 16:30 »

Код, блин, у него, грамотный....

https://rextester.com/ISZFW96211

Код
C++ (Qt)
void setParents(parent&& mother, parent&& father) noexcept = delete;

Этого недостаточно, нужно больше "delete" Улыбающийся. А так "просто", да. Никакой лишней писанины Улыбающийся.
Записан

Пока сам не сделаешь...
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #51 : Декабрь 07, 2019, 15:30 »

Это вы такое процентное соотношение придумали, зачем мне его понимать? Я считаю, что нормальный компилятор побольше может. По крайней избавит от получения висячего указателя на ровном месте.

чувак, ты нормальнывй вообще?
99,(9) данных известны только в рантайме.

Сложности с отслеживанием нити разговора?

да. мне сложно отслеживать направление твоей мысли по односложным ответам.
у меня создалось впечатление,
что направление твоей мысли - задом-на-перед.

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

ты же считаешь вещь полезной на случай,
если найдется такой идиот, который зачем то сначала решит использовать указатель,
а затем нужно ему помочь не налажать с nullptr

Что тогда предлагаете делать? Уповать на то, что другие догадаются, что автор задумал поле непустым, хотя там обычный указатель? Или комментарий для поля писать: "The m_parent must be not_null."? Улыбающийся


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

я уповаю на то, что нормальный здравомыслящий человек без труда догадается,
что ежели по задумке у ребенка всегда должен быть родитель,
то родитель никак не может быть nullptr.
и для этого даже в код смотреть не нужно.

в чем ущербность not_nul подхода?
глупо и бессмысленно пытаться делать защиту от идиотов.

Мда... не зря я недолюбливаю майкрософтовский компилятор  Веселый. Попробуйте собрать этот код в gcc или clang.
суть в том, что на практике в обоих случаях (и с ссылками, и с указателями) можно ничайно допустить ошибку.
суть в том, что есть простой способ превентивно не позволить программисту допустить ошибку.

способ простой и кросс-платформенный.
просто используй ссылки там,
где по смыслу нужно работать с реальным объектом,
и если объект не должен быть временным - просто запрети временные.

обратил внимание: звучит как Капитанство?

Ага, каждый раз писать лабуду типа "static_assert(!::std::is_rvalue_reference<parent&&>::value, "temporary objects prohibited"); это "просто" Улыбающийся.

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

вот пример из моего реального кода.
он очень простенький, но иллюстрирует идею.

Код:
    namespace detail
    {
        template<class s, dfor_lvalue_string(s&&)>
        constexpr decltype(auto) c_str(s&& text) noexcept
            { return text.c_str(); }

        template<class s, dfor_array_of_symbols(s)>
        constexpr decltype(auto) c_str(const s& arr) noexcept
            { return arr; }

        template<class s, dfor_pointer_to_symbol(s)>
        constexpr auto c_str(const s& ptr) noexcept -> const auto*
        {
            assert(ptr);
            return ptr;
        }

    } //namespace detail

обрати внимание: тут не статик_ассерт,
здесь концепт, основанный на SFINAE

если бы чувак смог ничайно запихать временную строку,
то в результате он бы получил указатель на уже протухший объект.

однако концепт dfor_lvalue_string исключает функцию из компиляции
если ей передать rvalue. ничайная ошибка полностью исключена.

и вот подобные концепты я использую каждый раз,
когда у функции есть какие либо важные ограничения.

это не просто увеличивает читабельность кода.
это позволяет исключить саму возможность ошибки.

концепты позволяют недопустить саму возможность возникновения ошибки.
а не пытаются как твой not_null пытаться бороться с последствиями идиотизма.



По публичному интерфейсу:
Код
C++ (Qt)
struct child
{
   template<class parent>
   child(parent&& mother, parent&& father) noexcept;
 
   template<class parent>
   void setParents(parent&& mother, parent&& father) noexcept;
 
   ...
};

пользователи класса подумают, что могут передавать объекты в любом виде (по значению, по lvalue ссылке, по rvalue ссылке)

нет, не подумают.
если, конечно, у пользователей нет шизофрении,
и они не больны клиническим идиотизмом.

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

вот нафига вообще может понадобится передавать ребенку временный объект родителя???
как такое вообще может прийти в голову?

от ничайных ошибок защитит статик-ассерт.


Напрямую коррелирует. Вариант с gsl::not_null вы, похоже, так и не пробовали. Даже интересно, как майкрософтовский компилятор там себя поведёт.

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

https://rextester.com/DPGZ73051

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

но ты можешь сам продемонстрировать:
предоставить код и ссылку на он-лайн компилятор.

онлайн rextester умеет жоссии, шланг, и студийный каэль.

Чтобы долго не объяснять, что такое "абстрактная фабрика". Поэтому и написал "такую абстрактную фабрику, как например QItemEditorFactory". Не именно её, а "такую как", чтобы был наглядный пример перед глазами. Не думал, что с пониманием этого предложения могут возникнуть сложности.

человек, ты идею то вразумел, нет?

тебе не нужен not_null, что бы гарантировать, что выданнный сторонним апи объект валидный.

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

И ФСЁЁЁ.


казадлось бы, и причем тут какие то абстрактные фабрики?
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #52 : Декабрь 07, 2019, 15:33 »

Те не менее, вопрос актуальный. Вы часто берёте адрес временного объекта?

не может этот вопрос быть аутальным,
потому что никто в здравом уме не берет адрес временного объекта.


с тобой всё в порядке?
может устал, отдохнуть надо?
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #53 : Декабрь 07, 2019, 15:35 »

пользователи класса подумают, что могут передавать объекты в любом виде (по значению, по lvalue ссылке, по rvalue ссылке), а на деле окажется что не в любом. Откуда они узнают, в каком виде нельзя передавать? Наугад перебирать разные типы и смотреть, скомпилируется или нет? Или придётся лезть в исходники этого "грамотного" кода и разбирать его? Очень "понятно и логично".

Код, блин, у него, грамотный....

https://rextester.com/ISZFW96211


и что тебе, блин, не понравилось в моём грамотной подходе?


твой вариант содержит деффект.
он пропустит баг, если скомбинировать rvalue и lvalue

Код:
    son.setParents(mother, parent{"Stepan"}); // <--- rvalue and lvalue


Код:
#include <iostream>
#include <string>

struct parent
{
    parent(const std::string& n)
        : name(n)
    {
        std::cout << "parent(" << name << "): was built\n";
    }
    
    ~parent()
    {
        std::cout << "parent(" << name << "): was destroyed\n";
    }
    std::string name;
};

struct child
{
    
    template<class parent>
    child(parent&& mother, parent&& father) noexcept
        : m_mother(), m_father()
    {
        this->setParents(
            ::std::forward<parent>(mother),
            ::std::forward<parent>(father)
        );
    }

    const parent& getMother() noexcept { return *this->m_mother; }
    const parent& getFather() noexcept { return *this->m_father; }

    void setParents(parent&& mother, parent&& father) noexcept = delete;
    void setParents(const parent& mother, const parent& father) noexcept
    {
        this->m_mother = &mother;
        this->m_father = &father;
    }

    void work()
    {
        std::cout << "mother: " << getMother().name << std::endl;
        std::cout << "father: " << getFather().name << std::endl;
    }

private:
    const parent* m_mother;
    const parent* m_father;
};

int main()
{
    parent mother{"Marfa"};
    parent father{"Miron"};

    child  son{mother, father};
    son.work();

    son.setParents(mother, parent{"Stepan"}); // <--- rvalue and lvalue
    son.work();

    return 0;
}

« Последнее редактирование: Декабрь 07, 2019, 15:58 от _Bers » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #54 : Декабрь 07, 2019, 16:06 »


и что тебе, блин, не понравилось в моём грамотной подходе?


твой вариант содержит деффект.
он пропустит баг, если скомбинировать rvalue и rvalue

Да, я был в курсе, когда писал код. Проблема в том, что шаблоны в этой задаче нахрен не нужны, как и статик ассерты.
Как и в последующих примерах.
А потом ноют что код компилируется сто лет и требуют модулей Веселый

А так, как я уже говорил, пример со ссылками высосан из пальца, он работает в ограниченном числе случаев и ровно никак не решает никаких проблем.

Код:
bool filterFoo(const Foo &foo) { ... } // ну ок, тут ссылка по делу
void processFoo(shared_ptr<Foo> foo)
{
    // вот вы предлагаете перед разыменованием проверять, ну ок, вполне себе подход
    if (!foo)
       return;
    // ну разыменовали, никакого UB, всё круто
    if (filterFoo(*foo))
       return;
    // чтобы понятно, почему нельзя заюзать ссылку изначально, форвардим сигналом дальше, скажем, в иной тред
    // ну или в мапу кладем
    emit fooProcessed(foo);
}

Ну ок, мы решили проблему filterFoo, но processFoo всё равно должен проверять указатель - ведь могут же nullptr передать?

Сравним с
Код:
bool filterFoo(const Foo &foo) { ... }
void processFoo(not_null<shared_ptr<Foo>> foo)
{
    if (filterFoo(*foo)) // оппа, это больше не UB!
       return;
    ....
}

Магия, как же так вышло-то?
UB ушло уровнем выше - там где мы в gsl::not_null<Foo *> положили Foo * не проверив его на 0:
Код:
shared_ptr<Foo> foo = getFoo();
processFoo(foo); // ooops!
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #55 : Декабрь 07, 2019, 16:30 »

Ага, каждый раз писать лабуду типа "static_assert(!::std::is_rvalue_reference<parent&&>::value, "temporary objects prohibited"); это "просто" Улыбающийся.

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

Характеристики варианта "ссылочная семантика" сменились с "простой, понятный и логичный" на "грамотный, надежный, качественный". Ибо, когда в сигнатуре функции из четырёх комбинаций параметров rvalue vs lvalue правильная только одна, а три приводят к ошибке, это не очень "понятно" и "логично". А каждый раз писать проверки - то уже и не "просто". Я вот предпочитаю, чтобы типы были выразительными, чтобы сразу было понятно что туда можно передавать, а что нельзя. И чтобы проверки были зашиты в типе, а не каждый раз их писать.

Кстати, как будете проверять виртуальные методы?
Код
C++ (Qt)
struct child
{
...
   virtual void setParents(not_null<parent> mother, not_null<parent> father) noexcept = 0;
...
};

Если вам лично не нужен unique_ptr, и как следствие not_null<unique_ptr>, это же не значит, что и другим они не нужны.
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #56 : Декабрь 07, 2019, 17:59 »

На самом деле, указатели просто более гибкие, чем ссылки. Пока ссылки не допилят до вменяемого уровня, все так и будут юзать указатели.

Вот табличка фич
Код:
# * &
0 + - // перегрузка операторов доступа содержимому объекта
1 + - // "умные" классы (следствие 0)
2 + - // возможность задавать семантику владения (следствие 1)
3 + - // опциональность (занулябельность), решается для ссылок типа optional<ref<T>>
4 - + // занулябельность, решается для указателей типа not_null<smart_ptr<T>>

То есть как видно ссылки проигрывают по фичам из-за отсутствия перегрузки оператора. (и "умных" ссылок со, гм, счетчиком ссылок).
При этом всё равно будут задачи, когда надо уметь положить nullptr, ака опциональная ссылка.
То есть, как ни крути, нужен либо not_null, либо optional_ref, в зависимости от выбранного поведения по умолчанию (разрешать ноль или запрещать).
Проблема в том, что ни того, ни другого в стандарте нет (ну типа можно сделать optional<ref<T>> и not_null<T*>, а что-то "умное" (ссылку/указатель) заюзать уже нельзя)
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #57 : Декабрь 07, 2019, 19:03 »

не может этот вопрос быть аутальным,
потому что никто в здравом уме не берет адрес временного объекта.

А вы в каком уме были, когда этот пример писали? Зачем добавили операцию взятия адреса временного объекта? Варианты с указателем и gsl::not_null препятствуют передаче временного объекта в функцию. Без всяких статик ассертов, и, что самое удивительное, в compile-time. Да даже с этим взятием адреса, два из трёх компиляторов выдают ошибку. Чтобы добиться такого же поведения с "ссылочной семантикой", необходимо изувечить API класса и вручную понадобавлять проверок. Если для вас "замени ссылки на указатели - получишь те же яйца.", то для меня получаются совсем разные варианты.
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #58 : Декабрь 07, 2019, 19:35 »

Пока ссылки не допилят до вменяемого уровня, все так и будут юзать указатели.

С ссылками всё нормально, не надо их допиливать.
What is a reference?
Цитировать
An alias (an alternate name) for an object.
...
Important note: Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object, just with another name. It is neither a pointer to the object, nor a copy of the object. It is the object. There is no C++ syntax that lets you operate on the reference itself separate from the object to which it refers.

Именно в таком качестве ссылки и нужно использовать. Когда функция принимает аргумент типа const Some&, то ожидается, что она будет "читать" этот объект или сделает его копию. Поэтому можно и временный объект передать, функция его скопирует и будет у неё not_null объект. Многие ли ожидают, что функция возьмёт адрес этого объекта для длительного хранения, чтобы потом к нему обратиться?

В ситуации с parent <-> child происходят несколько другие вещи. child'у не нужен сам объект parent, ему нужна ассоциативная связь с ним. Эту связь нужно отдельно создать. В самом простом случае, это взятие адреса объекта и помещение его в указатель. Так вот, с сигнатурой const Some& передаётся сам объект, и никто не разрешал создавать ассоциативные связи с ним. Впрочем, в текущем С++ никто и запретить не может Улыбающийся. Поэтому и появляются всякие "ссылочные семантики" с "грамотным" кодом.

Так что нужно допиливать именно указатели.
Записан

Пока сам не сделаешь...
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #59 : Декабрь 07, 2019, 20:39 »

Да, я был в курсе, когда писал код. Проблема в том, что шаблоны в этой задаче нахрен не нужны, как и статик ассерты.
Как и в последующих примерах.
А потом ноют что код компилируется сто лет и требуют модулей Веселый

А так, как я уже говорил, пример со ссылками высосан из пальца, он работает в ограниченном числе случаев и ровно никак не решает никаких проблем.

ничего не понятно.
во-первых, о каком таком нытье идет речь?

во-вторых, что значит пример со ссылками высосан из пальца и работает в ограниченном числе случаев,
и каких таких проблем он ровно никак не решает?

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

человек, ты о чем вообще?

Записан
Страниц: 1 2 3 [4] 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.148 секунд. Запросов: 22.