2010-12-27 13 views
23
struct X 
{ 
    X():mem(42){} 
    void f(int param = mem) //ERROR 
    { 
     //do something 
    } 
private: 
    int mem; 
}; 

¿Alguien me puede dar una razón por la cual esto es ilegal en C++ ?! Es decir, sé que es un error, sé lo que significa el error, simplemente no puedo entender por qué sería esto ilegal.Miembro no estático como argumento predeterminado de una función miembro no estática

+0

posible duplicado de [C++ miembro de la clase heredada públicamente no se puede utilizar como argumento predeterminado] (http://stackoverflow.com/questions/2159538/c-publicly-herited-class-member-cannot-be-used-as-default-argument) – marcog

+1

@marcog: Aunque puedo estar de acuerdo, esto está relacionado de alguna manera , pero creo que esto no es un duplicado en absoluto ... –

+0

Esto es problema de C++: D –

Respuesta

34

Su código (simplificado):

struct X 
{ 
    int mem; 
    void f(int param = mem); //ERROR 
}; 

Usted desea utilizar un conjunto de datos miembros no estáticos como valor predeterminado para un parámetro de una función miembro La primera pregunta que viene. a la mente es este: que instan específico ce de la clase pertenece al valor predeterminado mem?

X x1 = {100}; //mem = 100 
X x2 = {200}; //mem = 200 

x1.f(); //param is 100 or 200? or something else? 

Su respuesta podría ser 100 como f() se invoca en el objeto que tiene x1mem = 100. Si es así, entonces se requiere la aplicación de implementar f() como:

void f(X* this, int param = this->mem); 

que a su vez requiere que el primer argumento que ser inicializado antes de la inicialización de otro argumento. Pero el estándar de C++ no especifica ningún orden de inicialización de los argumentos de la función. Por lo tanto, eso no está permitido. Su por la misma razón que el C++ estándar no permite siquiera esto:

int f(int a, int b = a); //§8.3.6/9 

De hecho, § 8.3.6/9 dice explícitamente,

Los argumentos predeterminados se evalúan cada vez que se llama a la función. El orden de la evaluación de los argumentos de la función es no especificado. En consecuencia, los parámetros de una función no se deben usar en expresiones de argumentos por defecto, incluso si no se evalúan.

Y el resto de la sección es una lectura interesante.


Un tema interesante relacionado con argumentos "por defecto" (no relacionados con este tema, aunque):

+1

Aceptado para una explicación ordenada y lógica de la respuesta de @ user396672 –

+0

@Armen: Agregué un enlace a otro tema. : D – Nawaz

+0

Y para eso, también :) –

1

ISO C++ sección 8.3.6/9

un miembro no estática no deberá utilizarse en una expresión de argumento predeterminado, incluso si no se evalúa, a menos que aparece como la id-expresión de una expresión de acceso de miembro de clase (5.2.5) o a menos que se use para formar un puntero al miembro (5.3.1).

También consulte el ejemplo dado en esa sección.

+4

Sí, pero ¿POR QUÉ? –

+1

No, los parámetros predeterminados pueden no conocerse en tiempo de compilación. Por ejemplo, void f (int x = g()) donde g es una función global está OK –

1

Por una razón, porque f es público, pero mem es privado. Como tal, código como este:

int main() { 
    X x; 
    x.f(); 
    return 0; 
} 

... implicaría que el código externo recupere los datos privados de X.

Aparte de eso, podría (o al menos podría) también hacer la generación de código un poco complicado. Normalmente, si el compilador va a usar un argumento predeterminado, obtiene el valor que va a pasar como parte de la declaración de la función. Generar código para pasar ese valor como parámetro es trivial. Cuando podría estar pasando a un miembro de un objeto (posiblemente anidado arbitrariamente profundamente) y luego agregar cosas como la posibilidad de que sea un nombre dependiente en una plantilla, eso podría (por ejemplo) nombrar otro objeto con una conversión al objetivo correcto escriba, y tiene una receta para hacer la generación de código bastante difícil. No sé con certeza, pero sospecho que alguien pensó sobre cosas así, y decidió que era mejor mantenerse conservador, y posiblemente abierto se adelgaza más tarde, si se encontró una buena razón para hacerlo. Dada la cantidad de veces que he visto surgir problemas, supongo que seguirá siendo así por un largo tiempo, simplemente porque rara vez causa problemas.

+1

Esto está mal. 11/7 - "Los nombres en una expresión de argumento predeterminada (8.3.6) están vinculados en el punto de declaración, y el acceso se verifica en ese punto en lugar de en cualquier punto de uso ..." <- buscado después de verificar mi compilador particular. –

1

El compilador debe conocer las direcciones para mantener los valores predeterminados en tiempo de compilación. Las direcciones de las variables miembro no estáticas son desconocidas en tiempo de compilación.

+1

int add_random (int k, int m = rand()) {return k + m;} es legal aunque ni el valor aleatorio ni la dirección se conocen en tiempo de compilación.El valor predeterminado de Enum puede no tener dirección en absoluto. – user396672

+1

¿Estás seguro? Quiero decir, cada vez que empiezo un ejecutable, el parámetro m (que definiste como: m = rand()) tiene el mismo valor en mi máquina. Parece que se define una vez (en tiempo de compilación) y no cambia el valor en absoluto. Los enumerados son constantes, sus valores son conocidos en tiempo de compilación. – Vladimir

6

Los argumentos predeterminados deben conocerse en tiempo de compilación. Cuando habla de algo así como una invocación a una función, entonces la función se conoce en tiempo de compilación, incluso si el valor de retorno no es, por lo que el compilador puede generar ese código, pero cuando se establece de forma predeterminada en una variable miembro, el compilador no lo hace t saber dónde encontrar esa instancia en tiempo de compilación, lo que significa que efectivamente tendría que pasar un parámetro (this) para encontrar mem. Tenga en cuenta que no puede hacer algo como void func(int i, int f = g(i)); y los dos son efectivamente la misma restricción.

También creo que esta restricción es tonta. Pero luego, C++ está lleno de restricciones tontas.

+0

+1 para la observación de que la pregunta no está realmente relacionada con la clase o incluso con OO. Sin embargo, supongo que la restricción se refiere al contexto de evaluación en lugar del tiempo de evaluación – user396672

+0

+1 para restricciones tontas. – rubenvb

+3

-1 for '" Los argumentos predeterminados deben conocerse en tiempo de compilación "'. Esto no es verdad. – Nawaz

5

Como DeadMG ha mencionado anteriormente, somethig como

void func(int i, int f = g(i))

es ilegal por la misma razón. supongo, sin embargo, que no es simplemente una restricción tonta. Para permitir tal construcción, necesitamos restringir el orden de evaluación para los parámetros de función (ya que necesitamos calcular esto antes de esto-> mem), pero el estándar de C++ rechaza explícitamente cualquier suposición sobre el orden de evaluación.

0

Los argumentos predeterminados se evalúan en dos pasos distintos, en contextos diferentes.
Primero, la búsqueda de nombre para el argumento predeterminado se realiza en el contexto de la declaración.
En segundo lugar, la evaluación del argumento predeterminado se realiza en el contexto de la llamada a la función real.

Para evitar que la implementación sea demasiado complicada, se aplican algunas restricciones a las expresiones que se pueden usar como argumentos predeterminados.

  • No se pueden utilizar variables con duración no estática, ya que es posible que no existan en el momento de la llamada.
  • No se pueden usar variables miembro no estáticas porque necesitan una calificación (implícita) this->, que normalmente no se puede evaluar en el sitio de la llamada.
2

La respuesta aceptada en la pregunta es duplicado por qué, pero la norma también establece explícitamente qué esto es así:

8.3.6/9:

" Ejemplo: la declaración de está mal formada X :: mem1() en el siguiente ejemplo, porque no hay ningún objeto se suministra para el miembro de X no estático :: a utilizar como un inicializador.

int b; 
class X 
    int a; 
    int mem1(int i = a); // error: nonstatic member a 
          // used as default argument 
    int mem2(int i = b); // OK: use X::b 
    static int b; 
}; 

La declaración de X :: mem2() es significativa, sin embargo, ya que no se necesita ningún objeto para acceder al miembro estático X :: b. Clases, objetos y miembros se describen en la cláusula 9. "

... y puesto que no existe una sintaxis para suministrar el objeto necesario para resolver el valor de X::a en ese punto, que es efectivamente imposible utilizar miembro no estático . variables como inicializadores de los argumentos por defecto

1

Como todas las otras respuestas acaba de discutir el problema, pensé Yo publicaría una solución.

Tal como se utiliza en otros idiomas sin argumentos por defecto (por ejemplo, C# pre 4.0)

Sólo tiene que utilizar la sobrecarga para proporcionar el mismo resultado:

struct X 
{ 
    X():mem(42){} 
    void f(int param) 
    { 
     //do something 
    } 
    void f() 
    { 
     f(mem); 
    } 
private: 
    int mem; 
}; 
Cuestiones relacionadas