Функция шаблона члена класса, объявленная как friend в классе B, не может получить доступ к закрытым членам класса A (только Clang)
Пожалуйста, взгляните на этот фрагмент кода. Я знаю, что это не имеет большого смысла, оно просто предназначено для иллюстрации проблемы, с которой я сталкиваюсь:
#include <iostream>
using namespace std;
struct tBar
{
template <typename T>
void PrintDataAndAddress(const T& thing)
{
cout << thing.mData;
PrintAddress<T>(thing);
}
private:
// friend struct tFoo; // fixes the compilation error
template <typename T>
void PrintAddress(const T& thing)
{
cout << " - " << &thing << endl;
}
};
struct tFoo
{
friend void tBar::PrintDataAndAddress<tFoo>(const tFoo&);
private:
int mData = 42;
};
struct tWidget
{
int mData = 666;
};
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
bar.PrintDataAndAddress(tFoo()); // Compilation error
return 0;
}
Приведенный выше код вызывает следующую ошибку:
source_file.cpp: 10: 3: ошибка: 'PrintAddress' является частным членом 'tBar' PrintAddress (вещь); source_file.cpp: 42: 6: примечание: при создании шаблона функции > специализация 'tBar:: PrintDataAndAddress' запрошена здесь bar.PrintDataAndAddress(TFoo());//Ошибка компиляции source_file.cpp: 17: 7: note: объявлено приватным здесь void PrintAddress (const T & вещь)
но только в Clang++. GCC и MSVC отлично справляются с этим (вы можете быстро проверить это, вставив этот код в http://rextester.com/l/cpp_online_compiler_clang)
Кажется, что tBar::PrintDataAndAddress<tFoo>(const tFoo&)
использует тот же доступ, что и tFoo
, где он подружился. Я знаю это, потому что поддержка tFoo
в tBar
устраняет эту проблему. Проблема также исчезает, если tBar::PrintDataAndAddress
- функция без шаблона.
Я не смог найти что-либо в стандарте, объясняющем это поведение. Я считаю, что это может быть плохой интерпретацией 14.6.5 - temp.inject, но я не могу утверждать, что все прочитал.
Кто-нибудь знает, правильно ли Clang не смог скомпилировать вышеуказанный код? Можете ли вы указать соответствующий стандартный текст на С++, если это так?
Кажется, что для этой проблемы доступ к частному члену должен быть функцией шаблона. например, в приведенном выше примере, если
мы делаем PrintAddress функцией без шаблона, код будет компилироваться без ошибок.
Ответы
Ответ 1
Принуждение компилятора к экземпляру tBar::PrintDataAndAddress<tFoo>
перед его использованием решает проблему.
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....
bar.PrintDataAndAddress(tFoo()); // Now fine
return 0;
}
Кажется, это компилятор promlem, поскольку он выглядит очень похоже на это:
В С++ почему не удается сопоставить функцию-член класса шаблона с использованием типа шаблона другого класса?
Чтобы быть немного точнее... В строке bar.PrintDataAndAddress(tFoo());
компилятор должен инициализировать функцию-член tBar::PrintDataAndAddress<tFoo>
и в то же время он должен разрешить объявление друга. Это внутренне два отдельных шага. По-видимому, компилятор не делает этого в строгом порядке, когда он написан в одном выражении. Чтобы заставить компилятор создать экземпляр bar.PrintDataAndAddress(tFoo())
сначала путем доступа к указателю функции, эти два шага находятся в правильном порядке.
Ответ 2
Попробуйте добавить это перед функцией друга
template <typename tFoo>