Подтвердить что ты не робот

С++ 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
4b9b3361

Ответ 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