2010-09-07 2 views
5

Может кто-нибудь объяснить мне, почему этот код:Weird ошибка компилятора и наследование шаблонов

class safe_bool_base 
{ //13 
    protected: 

     typedef void (safe_bool_base::*bool_type)() const; 

     void this_type_does_not_support_comparisons() const {} //18 

     safe_bool_base() {} 
     safe_bool_base(const safe_bool_base&) {} 
     safe_bool_base& operator=(const safe_bool_base&) { return *this; } 
     ~safe_bool_base() {} 
}; 

template <typename T=void> class safe_bool : public safe_bool_base 
{ 
    public: 

     operator bool_type() const 
     { 
      return (static_cast<const T*>(this))->boolean_test() ? &safe_bool_base::this_type_does_not_support_comparisons : 0; 
     } 

    protected: 

     ~safe_bool() {} 
}; 

template <> class safe_bool<void> : public safe_bool_base 
{ 
    public: 

     operator bool_type() const 
     { 
      return (boolean_test() == true) ? &safe_bool_base::this_type_does_not_support_comparisons : 0; //46 
     } 

    protected: 

     virtual bool boolean_test() const = 0; 
     virtual ~safe_bool() {} 
}; 

Выдает следующую ошибку компилятора?

c:\project\include\safe_bool.hpp(46) : error C2248: 'safe_bool_base::this_type_does_not_support_comparisons' : cannot access protected member declared in class 'safe_bool_base' 
c:\project\include\safe_bool.hpp(18) : see declaration of 'safe_bool_base::this_type_does_not_support_comparisons' 
c:\project\include\safe_bool.hpp(13) : see declaration of 'safe_bool_base' 

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

Я что-то упустил?

+1

Это хороший вопрос. Предложите вам добавить тег 'protected', 'base', 'производный', а также вопрос для этого, чтобы он появился в поисках/ссылках – Chubsdad

+0

@ Chhsddad: Спасибо. Я могу добавить только еще один тег. (5 - максимальный счетчик тегов afaik.) – ereOn

ответ

9

Это, вероятно, поможет (воспроизводимая в не шаблон ситуации также)

struct A{ 
protected: 
    void f(){} 
}; 

struct B : A{ 
    void g(){&A::f;}  // error, due to Standard rule quoted below 
}; 

int main(){ 
} 

VS gives "'A::f' : cannot access protected member declared in class 'A'"

Для того же кода, Комео дает

"ComeauTest.c", line 7: error: protected function "A::f" (declared at line 3) is not accessible through a "A" pointer or object void g(){&A::f;} ^

"ComeauTest.c", line 7: warning: expression has no effect void g(){&A::f;}

Вот фиксированный код, который достигает желаемых целей

struct A{ 
protected: 
    void f(){} 
}; 

struct B : A{ 
    void g(){&B::f;}  // works now 
}; 

int main(){ 
} 

Итак, почему первый фрагмент кода не работает?

Это из следующего правила в C++ Standard03

11.5/1- "When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

Меняет возвращение в пределах функций оператора следующим

return (boolean_test() == true) ? &safe_bool<void>::this_type_does_not_support_comparisons : 0; //46 

return (static_cast<const T*>(this))->boolean_test() ? &typename safe_bool<T>::this_type_does_not_support_comparisons : 0; 

EDIT 2: Не обращайте внимания на мои объяснения. Давид прав. Вот к чему это сводится.

struct A{ 
protected: 
    int x; 
}; 

struct B : A{ 
    void f(); 
}; 

struct C : B{}; 

struct D: A{   // not from 'C' 
}; 

void B::f(){ 
    x = 2;   // it's own 'A' subobjects 'x'. Well-formed 

    B b; 
    b.x = 2;  // access through B, well-formed 

    C c; 
    c.x = 2;  // access in 'B' using 'C' which is derived from 'B', well-formed. 

    D d; 
    d.x = 2;  // ill-formed. 'B' and 'D' unrelated even though 'A' is a common base 
} 

int main(){} 
+0

Это звучит как правильный ответ! В идее, каково обоснование этого правила? –

+0

Вы должны указать «Если для доступа к элементу будет указатель на член, спецификатор вложенного имени должен называть производный класс», так как это указатель на член. –

+0

@Steve Jessop: Да, сделал полужирное форматирование правильно. Спасибо buddy – Chubsdad

1

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

class A 
{ 
    protected: 
     typedef void (A::*type)() const; 
     void foo() const {} 
}; 


class B : public A 
{ 
    public: 
     operator type() const 
     { 
      return &A::foo; 
     } 
}; 

Я считаю, что этот вопрос вы не можете вернуть член-указатели на функции к защищенным членам в открытом интерфейсе. (Edit: не так ...)

+0

Интересно. Но если я удалю последнее объявление шаблона, компилятор не будет жаловаться, и еще есть метод, который возвращает адрес 'safe_bool_base :: this_type_does_not_support_comparisons' – ereOn

+0

Кажется логичным. Предоставляя защищенный метод, вы указываете, что хотите только доверять своим соседям доступ к сундуку с сокровищами. В этом примере сосед просто дает кому-то ключ к сундуку с сокровищами (указатель на защищенный метод) – Patrick

+0

@Patrick: Я не согласен. Если это верно, оно должно также применяться к переменным-членам. Но, насколько я знаю, ничто не мешает мне вернуть адрес переменной 'private' или' protected' в публичном интерфейсе. – ereOn

0

Ответ Chubsdad разъясняет ваш вопрос о том, почему существует ошибка для специализации шаблона.

Теперь следующий C++ стандартное правило

14.7.2/11 Обычный доступ проверки правила не применяются к именам, используемых для определения явных
инстанциацию
. [Примечание. В частности, аргументы шаблона и имена, используемые в объявлении функции
(включая типы параметров, типы возвращаемых данных и спецификации исключений) могут быть
частных типов или объектов, которые обычно не были бы доступны, и шаблон может быть
элемент-член или функция-член, которая обычно не была бы доступна.- endnote]

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

Смежные вопросы