Неоднозначность shared_from_this
    
  ![]()  | 
Наши проекты:
 Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту  | 
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS | 
| [216.73.216.5] | 
 
 | 
		
  | 
    Неоднозначность shared_from_this
    
  | 
         
         
         
          
           Сообщ.
           #1
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Наткнулся тут на неоднозначность, причину которой не вполне понимаю. Есть такие классы: 
        
      ![]() ![]() 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 видеть не может.  | 
    
| 
         
         
         
          
           Сообщ.
           #2
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Не не может, а должен. Видимость и доступность друг с другом никак не связаны. Сам посуди: смена доступности вдруг изменила семантику кода. Или: ты хотел вызвать одну, но забыл или не учёл, что она недоступна, поэтому молча вызвалась другая. Итп. Чем меньше неявных решений принимает компилятор, тем надёжней код, потому что программист вовремя оповещается о возможных ошибках и заблуждениях.   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #3
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Значит если в вышеприведенном коде удалить наследование class B от std::enable_shared_from_this<B>, то ошибка все равно будет, но уже другая - о недоступности A::shared_from_this()?   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #4
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Ага. Разрешение перегрузки не учитывает доступность. Доступность проверяется только по факту использования.   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #5
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Qraizer @  Ага. Разрешение перегрузки не учитывает доступность. Доступность проверяется только по факту использования. Кстати, почему? Не могу вспомнить, что в D&E по этому поводу написано. Только про нежелание Страуструпа того, чтобы смена доступа влияла на семантику.  | 
    
| 
         
         
         
          
           Сообщ.
           #6
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Не могу вспомнить, что в D&E по этому поводу написано. Только про нежелание Страуструпа того, чтобы смена доступа влияла на семантику.  Об этом Саттер, вроде бы, писал. Но сходу не скажу, где.  | 
    
| 
         
         
         
          
           Сообщ.
           #7
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Но дык вот поэтому. Получится, что семантика зависит от кода, её не определяющего. Давай продолжим: 
        
      ![]() ![]() 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); P.S. Я бы сравнил зависимость разрешения перегрузки от атрибутов доступа с зависимостью порядка инициазизации агрегатов и подобъектов от порядка записи инициализаторов в заголовке конструктора.  | 
    
| 
         
         
         
          
           Сообщ.
           #8
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Qraizer @  Имеется очень веские основания подозревать, что программист (ошибочно) намеревался вызвать недоступный метод. Какие? На private вообще смотреть незачем... Добавлено Получается, что у нас даже закрытые члены остаются частью интерфейса класса.  | 
    
| 
         
         
         
          
           Сообщ.
           #9
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Та конечно  
        
        . В непосредственном производном private-класс ещё доступен, в следующем уже нет. Вот таким элегантным способом мы из A меняем семантику кода нашего потомка B. Добавлено Цитата D_KEY @  Формально - да. Это как файл в каталоге - правов нет, а имя видно.  Получается, что у нас даже закрытые члены остаются частью интерфейса класса.  | 
    
| 
         
         
         
          
           Сообщ.
           #10
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Qraizer @  Цитата D_KEY @  Формально - да. Это как файл в каталоге - правов нет, а имя видно.Получается, что у нас даже закрытые члены остаются частью интерфейса класса. Но я пока не понял преимущества этой дырки в инкапсуляции. Добавлено Цитата Qraizer @  Вот таким элегантным способом мы из A меняем семантику кода нашего потомка B. Ты это можешь сделать миллионом способов... Добавлено Цитата Qraizer @  В непосредственном производном private-класс ещё доступен, в следующем уже нет. Вот таким элегантным способом мы из A меняем семантику кода нашего потомка B. Лучше представь себе другую ситуацию: ![]() ![]() class A { public:      void f(); private:      ... }; class B { public:      void g(); private:      ... }; class C : public A, public B {     // берем открытую реализацию f из A, g из B ... }; Соответственно, имеем полное право написать: ![]() ![]() C c; c.f(); c.g(); Но мы не можем написать private(!) методы void g() в A и void f() в B... Иначе вызовы станут неоднозначными. Я не вижу в этом логики. Добавлено Я уже молчу про возможные проблемы с SFINAE...  | 
    
| 
         
         
         
          
           Сообщ.
           #11
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Дырка? С чего это это дырка? Назначение атрибутов доступа - не более чем в контроле соблюдения интерфейсных контрактов. Связывание имён к интерфейсам не имеет никакого отношения. 
        
      Компилятор должен сообщать обо всех нарушениях контрактов. Миллионом способов ты получишь диагностику. Конечно, ты можешь изменить всё, что душе вугодилось, даже сменить порядок вызова инициализации подобъектов и агрегатов, Плюсы вообще мало что запрещают абсолютно. Добавлено Цитата D_KEY @  Они и должны быть неознозначными. Ты (я, Flex Ferrum, компилятор, ... нужное подчекнуть) не знаешь, какие f() и g() хотел вызвать автор C. Я вот не вижу логики в противоположном поведении, имей оно место. 	 Иначе вызовы станут неоднозначными. Я не вижу в этом логики.   | 
    
| 
         
         
         
          
           Сообщ.
           #12
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Qraizer @  Цитата D_KEY @  Они и должны быть неознозначными. Ты (я, Flex Ferrum, компилятор, ... нужное подчекнуть) не знаю, какие f() и g() хотел вызвать автор C.Иначе вызовы станут неоднозначными. Я не вижу в этом логики.  Автор C ничего не знает о private-частях своих предков. Иначе это не private.  | 
    
| 
         
         
         
          
           Сообщ.
           #13
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          P.S. Если хочется полной инкапсуляции, а не контролируемой атрибутами, всегда есть PImpl.   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #14
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Но я пока не понял преимущества этой дырки в инкапсуляции.  Хотя бы вычисление sizeof. А так - да, это дырка из-за отсутствия модульности. Но imho без модулей это решение Бьярна логично.  | 
    
| 
         
         
         
          
           Сообщ.
           #15
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Qraizer @  Они и должны быть неознозначными. Т.е. проблемы у клиентов наследников, вызванные изменением закрытой части базового класса, тебе кажутся абсолютно логичными? Добавлено Цитата MyNameIsIgor @  Хотя бы вычисление sizeof Эм. Мы о поиске имен.  | 
    
| 
         
         
         
          
           Сообщ.
           #16
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Эм. Мы о поиске имен. Тебе нужно видеть из h-файла все приватные мемберы, чтобы сказать о размере. Ты хочешь двух правил для мемберов-полей и мемберов-функций? Добавлено Иными словами наследники и клиенты класса и так не защищены от изменения приватной части. Так чего ты ещё хочешь?  | 
    
| 
         
         
         
          
           Сообщ.
           #17
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата MyNameIsIgor @  Цитата D_KEY @  Эм. Мы о поиске имен. Тебе нужно видеть из h-файла все приватные мемберы, чтобы сказать о размере. Ты хочешь двух правил для мемберов-полей и мемберов-функций? Так "видь" размер. Какое это отношение имеет к доступности имен?  | 
    
| 
         
         
         
          
           Сообщ.
           #18
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  В том то и дело, что знает. И в этом отличительная черта объектой модели Плюсов. Ты можешь обойти инкапсуляцию, если очень надо, и иначе никак. Это будет говнокод, но не всё говнокод, что так выглядит, иногда говнокодом является не он сам, а его основополагающая база, и написанный тобой говнокод - это едиственный способ решить проблему. В случее PImpl такого в принципе невозможно будет сделать. Выбор как всегда на нами, а не языком.  Автор C ничего не знает о private-частях своих предков. Иначе это не private.   | 
    
| 
         
         
         
          
           Сообщ.
           #19
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Так "видь" размер. Какое это отношение имеет к доступности имен? Так они не доступны   Ты не можешь их вызвать и обратиться к ним.   | 
    
| 
         
         
         
          
           Сообщ.
           #20
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Я не говорю о Pimpl. Речь о том, что спецификаторы доступа должны влиять на поиск имен. Клиенту должны быть недоступны имена из закрытой части.   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #21
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Клиенту должны быть недоступны имена из закрытой части. Они и так недоступны! Ты же хочешь, чтобы они были невидимы. Т.е. превратить спецификаторы доступа в спецификаторы видимости.  | 
    
| 
         
         
         
          
           Сообщ.
           #22
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата MyNameIsIgor @  Цитата D_KEY @  Так "видь" размер. Какое это отношение имеет к доступности имен? Так они не доступны   Ты не можешь их вызвать и обратиться к ним.Недоступны мемберы. А имена - доступны. См. мой пример с неоднозначностью из-за добавления закрытого метода в базовый класс. Представь себе SFINAE с выбором специализации, допустим, по наличию внутреннего типа. Если у тебя будет соответствующий private-тип то SFINAE просто не сработает, а код не скомпилируется... Хотя надо попробовать      | 
    
| 
         
         
         
          
           Сообщ.
           #23
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Недоступны мемберы. А имена - доступны. Так вот и давай говорить о доступности и о видимости, ok? Цитата D_KEY @  Представь себе SFINAE с выбором специализации, допустим, по наличию внутреннего типа. Если у тебя будет соответствующий private-тип то SFINAE просто не сработает, а код не скомпилируется... Да, я в курсе. И?  | 
    
| 
         
         
         
          
           Сообщ.
           #24
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата MyNameIsIgor @  Цитата D_KEY @  Недоступны мемберы. А имена - доступны. Так вот и давай говорить о доступности и о видимости, ok? Имена-то доступны   Ладно, ок, давно плюсовую литературу не читал   Цитата  Да, я в курсе. И? Почему это правильно(если в интерфейсе класса этого самого внутреннего типа нет) и ради чего, собственно?  | 
    
| 
         
         
         
          
           Сообщ.
           #25
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Почему это правильно(если в интерфейсе класса этого самого внутреннего типа нет) и ради чего, собственно? Нужно освежить по D&E, там этому не мало посвящено. Просто мне кажется это логичным при механизме текстовых include'ов - мы и так всё видим, зачем делать вид, что нет?  | 
    
| 
         
         
         
          
           Сообщ.
           #26
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата MyNameIsIgor @  Нужно освежить по D&E, там этому не мало посвящено. Да, я вот тоже вспомнить не могу... Сегодня посмотрю   Цитата  Просто мне кажется это логичным при механизме текстовых include'ов - мы и так всё видим, зачем делать вид, что нет? Потому, что это делает менее логичным поиск имен.  | 
    
| 
         
         
         
          
           Сообщ.
           #27
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата D_KEY @  Потому, что это делает менее логичным поиск имен. Вот у тебя возникали серьёзные проблемы или ты только сейчас так разошёлся?     | 
    
| 
         
         
         
          
           Сообщ.
           #28
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата MyNameIsIgor @  Вот у тебя возникали серьёзные проблемы Неа. Но я вообще не помню, чтобы мне как-то помогали private/protected/public. Это же не повод...     |