На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Неоднозначность shared_from_this
    Наткнулся тут на неоднозначность, причину которой не вполне понимаю. Есть такие классы:
    ExpandedWrap disabled
      class A : private std::enable_shared_from_this<A>
      {
      };
       
      class B: public A, private std::enable_shared_from_this<B>
      {
          void f()
          {
              shared_from_this(); // Тут ошибка -  reference to 'shared_from_this' is ambiguous
          }
      };

    Почему так происходит? Ведь A унаследован от enable_shared_from_this приватно, а значит B shared_from_this видеть не может.
      Не не может, а должен. Видимость и доступность друг с другом никак не связаны. Сам посуди: смена доступности вдруг изменила семантику кода. Или: ты хотел вызвать одну, но забыл или не учёл, что она недоступна, поэтому молча вызвалась другая. Итп. Чем меньше неявных решений принимает компилятор, тем надёжней код, потому что программист вовремя оповещается о возможных ошибках и заблуждениях.
        Значит если в вышеприведенном коде удалить наследование class B от std::enable_shared_from_this<B>, то ошибка все равно будет, но уже другая - о недоступности A::shared_from_this()?
          Ага. Разрешение перегрузки не учитывает доступность. Доступность проверяется только по факту использования.
            Цитата Qraizer @
            Ага. Разрешение перегрузки не учитывает доступность. Доступность проверяется только по факту использования.

            Кстати, почему? Не могу вспомнить, что в D&E по этому поводу написано. Только про нежелание Страуструпа того, чтобы смена доступа влияла на семантику.
              Цитата D_KEY @
              Не могу вспомнить, что в D&E по этому поводу написано. Только про нежелание Страуструпа того, чтобы смена доступа влияла на семантику.

              Об этом Саттер, вроде бы, писал. Но сходу не скажу, где.
                Но дык вот поэтому. Получится, что семантика зависит от кода, её не определяющего. Давай продолжим:
                ExpandedWrap disabled
                  class Z { void f(int); };
                   
                  class A : protected Z {};
                   
                  class B : public A, public Z
                  {
                     using A::f;
                   
                  public:
                     void f(float);
                  };
                   
                  B b;
                   
                  b.f(123);
                Хуже всего то, что компилятор не знает, что имел в виду программист, когда писал такой вызов. Имеется очень веские основания подозревать, что программист (ошибочно) намеревался вызвать недоступный метод. Ещё интересней заменить protected на private. Был производный "реализован посредством" базового, а позже базовый стал недокументируемой деталью реализации. Надеюсь не надо объяснять, что атрибут наследования ну вообще к семантике перегрузки методов не имеет никакого отношения.

                P.S. Я бы сравнил зависимость разрешения перегрузки от атрибутов доступа с зависимостью порядка инициазизации агрегатов и подобъектов от порядка записи инициализаторов в заголовке конструктора.
                  Цитата Qraizer @
                  Имеется очень веские основания подозревать, что программист (ошибочно) намеревался вызвать недоступный метод.

                  Какие? На private вообще смотреть незачем...

                  Добавлено
                  Получается, что у нас даже закрытые члены остаются частью интерфейса класса.
                    Та конечно >:( . В непосредственном производном private-класс ещё доступен, в следующем уже нет. Вот таким элегантным способом мы из A меняем семантику кода нашего потомка B.

                    Добавлено
                    Цитата D_KEY @
                    Получается, что у нас даже закрытые члены остаются частью интерфейса класса.
                    Формально - да. Это как файл в каталоге - правов нет, а имя видно.
                      Цитата Qraizer @
                      Цитата D_KEY @
                      Получается, что у нас даже закрытые члены остаются частью интерфейса класса.
                      Формально - да. Это как файл в каталоге - правов нет, а имя видно.

                      Но я пока не понял преимущества этой дырки в инкапсуляции.

                      Добавлено
                      Цитата Qraizer @
                      Вот таким элегантным способом мы из A меняем семантику кода нашего потомка B.

                      Ты это можешь сделать миллионом способов...

                      Добавлено
                      Цитата Qraizer @
                      В непосредственном производном private-класс ещё доступен, в следующем уже нет. Вот таким элегантным способом мы из A меняем семантику кода нашего потомка B.

                      Лучше представь себе другую ситуацию:
                      ExpandedWrap disabled
                        class A
                        {
                        public:
                             void f();
                         
                        private:
                             ...
                        };
                         
                        class B
                        {
                        public:
                             void g();
                         
                        private:
                             ...
                        };
                         
                        class C : public A, public B
                        {
                            // берем открытую реализацию f из A, g из B
                        ...
                        };

                      Соответственно, имеем полное право написать:
                      ExpandedWrap disabled
                        C c;
                        c.f();
                        c.g();


                      Но мы не можем написать private(!) методы void g() в A и void f() в B... Иначе вызовы станут неоднозначными. Я не вижу в этом логики.

                      Добавлено
                      Я уже молчу про возможные проблемы с SFINAE...
                        Дырка? С чего это это дырка? Назначение атрибутов доступа - не более чем в контроле соблюдения интерфейсных контрактов. Связывание имён к интерфейсам не имеет никакого отношения.
                        Компилятор должен сообщать обо всех нарушениях контрактов. Миллионом способов ты получишь диагностику. Конечно, ты можешь изменить всё, что душе вугодилось, даже сменить порядок вызова инициализации подобъектов и агрегатов, Плюсы вообще мало что запрещают абсолютно.

                        Добавлено
                        Цитата D_KEY @
                        Иначе вызовы станут неоднозначными. Я не вижу в этом логики.
                        Они и должны быть неознозначными. Ты (я, Flex Ferrum, компилятор, ... нужное подчекнуть) не знаешь, какие f() и g() хотел вызвать автор C. Я вот не вижу логики в противоположном поведении, имей оно место.
                        Сообщение отредактировано: Qraizer -
                          Цитата Qraizer @
                          Цитата D_KEY @
                          Иначе вызовы станут неоднозначными. Я не вижу в этом логики.
                          Они и должны быть неознозначными. Ты (я, Flex Ferrum, компилятор, ... нужное подчекнуть) не знаю, какие f() и g() хотел вызвать автор C.

                          Автор C ничего не знает о private-частях своих предков. Иначе это не private.
                            P.S. Если хочется полной инкапсуляции, а не контролируемой атрибутами, всегда есть PImpl.
                              Цитата D_KEY @
                              Но я пока не понял преимущества этой дырки в инкапсуляции.

                              Хотя бы вычисление sizeof. А так - да, это дырка из-за отсутствия модульности. Но imho без модулей это решение Бьярна логично.
                              Сообщение отредактировано: MyNameIsIgor -
                                Цитата Qraizer @
                                Они и должны быть неознозначными.

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

                                Добавлено
                                Цитата MyNameIsIgor @
                                Хотя бы вычисление sizeof

                                Эм. Мы о поиске имен.
                                  Цитата D_KEY @
                                  Эм. Мы о поиске имен.

                                  Тебе нужно видеть из h-файла все приватные мемберы, чтобы сказать о размере. Ты хочешь двух правил для мемберов-полей и мемберов-функций?

                                  Добавлено
                                  Иными словами наследники и клиенты класса и так не защищены от изменения приватной части. Так чего ты ещё хочешь?
                                    Цитата MyNameIsIgor @
                                    Цитата D_KEY @
                                    Эм. Мы о поиске имен.

                                    Тебе нужно видеть из h-файла все приватные мемберы, чтобы сказать о размере. Ты хочешь двух правил для мемберов-полей и мемберов-функций?

                                    Так "видь" размер. Какое это отношение имеет к доступности имен?
                                      Цитата D_KEY @
                                      Автор C ничего не знает о private-частях своих предков. Иначе это не private.
                                      В том то и дело, что знает. И в этом отличительная черта объектой модели Плюсов. Ты можешь обойти инкапсуляцию, если очень надо, и иначе никак. Это будет говнокод, но не всё говнокод, что так выглядит, иногда говнокодом является не он сам, а его основополагающая база, и написанный тобой говнокод - это едиственный способ решить проблему. В случее PImpl такого в принципе невозможно будет сделать. Выбор как всегда на нами, а не языком.
                                        Цитата D_KEY @
                                        Так "видь" размер. Какое это отношение имеет к доступности имен?

                                        Так они не доступны :D Ты не можешь их вызвать и обратиться к ним.
                                          Я не говорю о Pimpl. Речь о том, что спецификаторы доступа должны влиять на поиск имен. Клиенту должны быть недоступны имена из закрытой части.
                                            Цитата D_KEY @
                                            Клиенту должны быть недоступны имена из закрытой части.

                                            Они и так недоступны! Ты же хочешь, чтобы они были невидимы. Т.е. превратить спецификаторы доступа в спецификаторы видимости.
                                              Цитата MyNameIsIgor @
                                              Цитата D_KEY @
                                              Так "видь" размер. Какое это отношение имеет к доступности имен?

                                              Так они не доступны :D Ты не можешь их вызвать и обратиться к ним.

                                              Недоступны мемберы. А имена - доступны. См. мой пример с неоднозначностью из-за добавления закрытого метода в базовый класс.
                                              Представь себе SFINAE с выбором специализации, допустим, по наличию внутреннего типа. Если у тебя будет соответствующий private-тип то SFINAE просто не сработает, а код не скомпилируется... Хотя надо попробовать :D
                                                Цитата D_KEY @
                                                Недоступны мемберы. А имена - доступны.

                                                Так вот и давай говорить о доступности и о видимости, ok?
                                                Цитата D_KEY @
                                                Представь себе SFINAE с выбором специализации, допустим, по наличию внутреннего типа. Если у тебя будет соответствующий private-тип то SFINAE просто не сработает, а код не скомпилируется...

                                                Да, я в курсе. И?
                                                  Цитата MyNameIsIgor @
                                                  Цитата D_KEY @
                                                  Недоступны мемберы. А имена - доступны.

                                                  Так вот и давай говорить о доступности и о видимости, ok?

                                                  Имена-то доступны :) Ладно, ок, давно плюсовую литературу не читал ;)

                                                  Цитата
                                                  Да, я в курсе. И?

                                                  Почему это правильно(если в интерфейсе класса этого самого внутреннего типа нет) и ради чего, собственно?
                                                    Цитата D_KEY @
                                                    Почему это правильно(если в интерфейсе класса этого самого внутреннего типа нет) и ради чего, собственно?

                                                    Нужно освежить по D&E, там этому не мало посвящено.
                                                    Просто мне кажется это логичным при механизме текстовых include'ов - мы и так всё видим, зачем делать вид, что нет?
                                                      Цитата MyNameIsIgor @
                                                      Нужно освежить по D&E, там этому не мало посвящено.

                                                      Да, я вот тоже вспомнить не могу... Сегодня посмотрю :)

                                                      Цитата
                                                      Просто мне кажется это логичным при механизме текстовых include'ов - мы и так всё видим, зачем делать вид, что нет?

                                                      Потому, что это делает менее логичным поиск имен.
                                                        Цитата D_KEY @
                                                        Потому, что это делает менее логичным поиск имен.

                                                        Вот у тебя возникали серьёзные проблемы или ты только сейчас так разошёлся? :D
                                                          Цитата MyNameIsIgor @
                                                          Вот у тебя возникали серьёзные проблемы

                                                          Неа. Но я вообще не помню, чтобы мне как-то помогали private/protected/public. Это же не повод... :D
                                                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                          0 пользователей:


                                                          Рейтинг@Mail.ru
                                                          [ Script execution time: 0,1098 ]   [ 16 queries used ]   [ Generated: 4.11.25, 07:45 GMT ]