На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Неоднозначность 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

                                Эм. Мы о поиске имен.
                                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0758 ]   [ 15 queries used ]   [ Generated: 4.11.25, 12:31 GMT ]