class safe_bool_base {
protected:
void this_type_does_not_support_comparisons() const {}
};
template <typename T=void> class safe_bool : public safe_bool_base {
public:
void func() {
&safe_bool::this_type_does_not_support_comparisons;
&safe_bool_base::this_type_does_not_support_comparisons;
}
};
template<> class safe_bool<void> : public safe_bool_base {
public:
void func() {
&safe_bool::this_type_does_not_support_comparisons;
&safe_bool_base::this_type_does_not_support_comparisons;
}
};
Сообщение об ошибке:
zzz.cpp: In member function 'void safe_bool<void>::func()':
zzz.cpp:7:10: error: 'void safe_bool_base::this_type_does_not_support_comparison
s() const' is protected
void this_type_does_not_support_comparisons() const {}
^
zzz.cpp:22:24: error: within this context
&safe_bool_base::this_type_does_not_support_comparisons;
^
Интересно, почему защищенный член нельзя посетить в шаблоне специализации. Коды не имеют смысла и просто для тестирования.
Когда public наследуется от базового класса, его защищенные члены становятся «защищенными членами производного класса, к которым можно получить доступ в функциях-членах производного класса». Обратите внимание, что они доступны только через сам производный класс (и его производные классы). Но защищенные члены не могут быть доступны через базовый класс. Вот почему &safe_bool::this_type_does_not_support_comparisons;
работает но &safe_bool_base::this_type_does_not_support_comparisons;
не делает.
Из стандарта, $ 11,4 / 1 Защищенный членский доступ
[Class.protected]:
(подчеркни мой)
Дополнительная проверка доступа помимо описанных ранее в пункте
[class.access] применяется, когда нестатический элемент данных или нестатический
функция-член является защищенным членом своего класса именования
([class.access.base]) 114 Как описано ранее, доступ к защищенному
участник предоставлен, потому что ссылка встречается у друга или участника
какого-то класса C. Если доступ заключается в формировании указателя на член
([expr.unary.op]), спецификатор вложенного имени должен обозначать C или класс
полученный из C. Все другие доступы включают (возможно, неявный)
выражение объекта ([expr.ref]). В этом случае класс объекта
выражение должно быть C или классом, производным от C. [Пример:class B { protected: int i; static int j; }; class D1 : public B { }; class D2 : public B { friend void fr(B*,D1*,D2*); void mem(B*,D1*); }; ... void D2::mem(B* pb, D1* p1) { pb->i = 1; // ill-formed p1->i = 2; // ill-formed i = 3; // OK (access through this) B::i = 4; // OK (access through this, qualification ignored) int B::* pmi_B = &B::i; // ill-formed int B::* pmi_B2 = &D2::i; // OK j = 5; // OK (because j refers to static member) B::j = 6; // OK (because B::j refers to static member) } ...
— конец примера]
Обратите внимание на утверждение int B::* pmi_B = &B::i; // ill-formed
в примере кода из стандарта, в основном это тот же случай вашего кода. Кстати, это не имеет ничего общего с шаблонной специализацией.
Других решений пока нет …