En el siguiente código, hay dos llamadas "equivalentes" a std::for_each
utilizando expresiones boost:bind
. La línea indicada compila, la línea de falla indicada falla. La mejor explicación que puedo encontrar en el estándar equivale a "porque así lo dijimos". Estoy buscando "por qué el estándar indica este comportamiento". Mis suposiciones están abajo.boost :: bind with protected members & context
Mi pregunta es sencilla: ¿Por qué la línea indicada no se compila y la siguiente línea no se puede compilar (y no quiero porque "el estándar lo dice", ya sé que - No aceptaré ninguna respuesta que dar esto como una explicación; me gustaría una explicación en cuanto a por qué el estándar lo dice).
Notas: Aunque uso boost, boost es irrelevante para esta pregunta, y el error en varios formatos se ha reproducido usando g ++ 4.1. * Y VC7.1.
#include <boost/bind.hpp>
#include <iostream>
#include <map>
#include <algorithm>
class Base
{
protected:
void foo(int i)
{ std::cout << "Base: " << i << std::endl; }
};
struct Derived : public Base
{
Derived()
{
data[0] = 5;
data[1] = 6;
data[2] = 7;
}
void test()
{
// Compiles
std::for_each(data.begin(), data.end(),
boost::bind(&Derived::foo, this,
boost::bind(&std::map<int, int>::value_type::second, _1)));
// Fails to compile - why?
std::for_each(data.begin(), data.end(),
boost::bind(&Base::foo, this,
boost::bind(&std::map<int, int>::value_type::second, _1)));
}
std::map<int, int> data;
};
int main(int, const char**)
{
Derived().test();
return 0;
}
la línea indicada falla con este error: MAIN.C: En función miembro 'vacío Derivado :: test()': MAIN.C: 9: error: 'vacío Base :: foo (int) 'está protegido main.C: 31: error: dentro de este contexto
Como se indicó anteriormente, la declaración supuestamente equivalente anterior se compila limpiamente (y si la declaración ofensiva está comentada, se ejecuta con el resultado esperado de imprimir "5" , "6", "7" en líneas separadas).
bien la búsqueda de una explicación, me encontré con 11.5.1 de la norma (en concreto, estoy mirando el proyecto 2006-11-06):
An additional access check beyond those described earlier in clause 11 is applied when a non-static data member or nonstatic member function is a protected member of its naming class (11.2)105) As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall name C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.
Después de leer esto, se hizo evidente por qué la segunda declaración falló mientras que la primera tuvo éxito, pero luego surgió la pregunta: ¿Cuál es la razón de ser de esto?
Mi idea inicial fue que el compilador estaba expandiendo las plantillas boost :: bind, descubriendo que Base :: foo estaba protegido y pateándolo porque boost :: bind < ...> no era un amigo. Pero, cuanto más pensaba en esta explicación, menos tenía sentido, porque si recuerdo correctamente, tan pronto como toma el puntero a un miembro (suponiendo que esté inicialmente dentro del control de acceso del miembro), toda la información de control de acceso es perdido (es decir, podría definir una función que devuelve un puntero arbitrario a un miembro que alternativamente devuelve un miembro público, protegido o privado, dependiendo de alguna entrada, y el que regresa no sabría nada).
Más lo pensé, y la única explicación plausible que pude dar con por qué debería marcar la diferencia fue en el caso de la herencia múltiple. Específicamente, dependiendo del diseño de la clase, el puntero del miembro cuando se calcule desde Base sería diferente al calculado desde Derivado.
Notaré de inmediato: es probable que necesite un mejor tema. Si alguien puede darme uno mejor, lo cambiaré. Gracias por adelantado. –
Porque el estándar lo dice. –
Parece que 'Base :: foo' debe tener' virtual'. ¿Funciona si agregas eso? – zildjohn01