С++ 11: Почему шаблон частного участника доступен вне класса?
Мне просто удалось обнаружить, что доступ к закрытому классу частного шаблона можно получить непосредственно за пределами закрывающего класса с помощью директивы using
:
class wrapper
{
private:
template <typename T>
class __tklass {};
class __klass {};
};
template <typename T>
using tklass = wrapper::__tklass<T>; // Expected error but compiles OK
// using klass = wrapper::__klass; // "Error: __klass is private"
int main()
{
tklass<int> v1; // Expected error but compiles OK
// wrapper::__tklass<int> v3; // "Error: __tklass is private"
// wrapper::__klass v4; // "Error: __klass is private"
}
Строки с пометкой "Ошибка: __xxx является конфиденциальной" правильно сообщают об ошибке при раскомментировании. Но строки с tklass
скомпилированы без каких-либо претензий от компилятора.
Итак, почему именно флаг компилятора tklass
как ошибка, несмотря на то, что wrapper::__tklass
является закрытым? Возможно ли это по стандарту? Если это так, разве это не считается серьезным нарушением прав доступа?
Я попробовал это на gcc-4.9.2, clang-3.5.0 и visual studio 2013 express. Командная строка GCC:
g++ -std=c++11 -pedantic -Wall -Wextra -Wshadow myfile.cpp
Ответы
Ответ 1
Это определенно ошибка компилятора, и на самом деле тот, который известен довольно давно: GCС# 47346 (сначала сообщается в январе 2011 года) и Clang # 15914 (впервые сообщалось в мае 2013 года). Ваш __tklass
явно private
, а псевдоним шаблона не помечен friend
, поэтому это должна быть простая ошибка доступа.
Простейшее воспроизведение происходит из приложения примера Clang, эта версия компилируется как для gcc 4.9.2, так и для clang 3.5.0, хотя определенно не скомпилировать ни один из них:
class A
{
class B {};
};
template<typename>
using T = A::B;
T<void> t;
Clang строго лучше, чем GCC на этом фронте, однако, поскольку эта конкретная ошибка, похоже, встречается только с псевдонимами шаблонов. "Обходной путь" (если вам нужна такая вещь для случаев, когда компилятор допускает неправильное...) было бы вернуться к предварительному сглаживанию шаблона pre-С++ 11:
template <typename>
struct T {
using type = A::B;
};
T<void>::type t;
Этот код правильно не компилируется с помощью clang (ошибка: "B" является частным членом "A" ), но все еще компилируется с gcc.
Ответ 2
Herb Sutter давно написал статью о том, как функции-члены шаблона могут предоставить задний ход в класс:
http://www.gotw.ca/gotw/076.htm (PLS проверит случай 4: "Адвокат языка", в конце)
Это может дать вам ответ.
РЕДАКТИРОВАТЬ: Мне любопытно, в чем были причины для голосования. Я цитирую статью:
"Является ли это дырой в механизме контроля доступа С++ и, следовательно, дырой в инкапсуляции С++?
Это демонстрирует интересное взаимодействие между двумя функциями С++: модель управления доступом и модель шаблона. Оказывается, что шаблоны-члены неявно "разрывают инкапсуляцию" в том смысле, что они эффективно предоставляют переносимый способ обойти механизм контроля доступа к классу ".
Кажется, для меня разумный ответ.
EDIT END
Можно было потратить много времени, пытаясь защитить интерфейсы техническими средствами, частными/защищенными и т.д. Мой предпочтительный способ - заключить соглашение между всеми разработчиками, чтобы хорошо использовать понятные правила, соответствующие наименее неожиданному подходу. (РЕДАКТИРОВАТЬ: И проверять код на эти правила с помощью сценариев code-review/reg-exp на регулярной основе)
"Не пытайтесь найти техническое решение для социальной проблемы" B. Stroustrup