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