c++ - La función de plantilla de miembro de clase A declarada como amigo en clase B no puede acceder a miembros privados de clase A(solo Clang)
c++11 templates (2)
Forzar al compilador a crear tBar::PrintDataAndAddress<tFoo>
instancia de tBar::PrintDataAndAddress<tFoo>
antes de usarlo resuelve el problema.
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....
bar.PrintDataAndAddress(tFoo()); // Now fine
return 0;
}
Parece ser un compilador promlem ya que se parece bastante a esto:
Para ser un poco más precisos ... En la bar.PrintDataAndAddress(tFoo());
de bar.PrintDataAndAddress(tFoo());
el compilador debe instanciar la función miembro tBar::PrintDataAndAddress<tFoo>
y al mismo tiempo debe resolver la declaración de amigo. Que son internamente dos pasos separados. Aparentemente el compilador no lo hace en el orden correcto cuando está escrito en una expresión. Para forzar al compilador a crear bar.PrintDataAndAddress(tFoo())
instancia de bar.PrintDataAndAddress(tFoo())
primero mediante el acceso al puntero de función, estos dos pasos están en el orden correcto.
Por favor, eche un vistazo a este fragmento de código. Sé que no tiene mucho sentido, solo pretende ilustrar el problema que estoy encontrando:
#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;
}
El código anterior dispara el siguiente error:
source_file.cpp: 10: 3: error: ''PrintAddress'' es un miembro privado de ''tBar'' PrintAddress (cosa) ; source_file.cpp: 42: 6: note: en la creación de instancias de la plantilla de función> especialización ''tBar :: PrintDataAndAddress'' solicitada aquí bar.PrintDataAndAddress (tFoo ()); // Error de compilación source_file.cpp: 17: 7: nota: declarado privado aquí void PrintAddress (const T & thing)
pero solo en Clang ++. GCC y MSVC están de acuerdo con esto (puedes probar eso rápidamente pegando ese código en http://rextester.com/l/cpp_online_compiler_clang )
Parece como si tBar::PrintDataAndAddress<tFoo>(const tFoo&)
esté usando el mismo acceso que tFoo
, donde es amigo. Lo sé porque entablar amistad con tFoo
en tBar
corrige este problema. El problema también desaparece si tBar::PrintDataAndAddress
es una función que no es de plantilla.
No he podido encontrar nada en el Estándar que explique este comportamiento. Creo que podría ser una mala interpretación de 14.6.5 - temp.inject, pero no puedo decir que lo haya leído todo.
¿Alguien sabe si Clang tiene razón al no compilar el código anterior? ¿Puede citar el texto estándar de C ++ relevante si ese es el caso?
Parece que para que ocurra este problema, el miembro privado al que se accede debe ser una función de plantilla. Por ejemplo, en el ejemplo anterior, si hacemos que PrintAddress sea una función que no sea de plantilla, el código se compilará sin errores.
Intenta agregar esto antes de la función de amigo
template <typename tFoo>