2011-02-21 14 views
13

Estoy atascado leyendo la descripción de std::bind en N3225, en la subsección 20.8.10.1. Dice lo siguiente debe imprimir 1, pero pensé que se supone que bind copia sus argumentos y por lo tanto debe imprimir 0. Si uno quiere referirse al argumento pasado, uno necesita usar std::ref, ¿verdad?¿Qué diablos hace std :: bind (x, y) do?

void f(int &a) { a = 1; } 

int main() { 
    int a = 0; 
    std::bind(f, a)(); 
    std::cout << a << std::endl; 
} 

salidas del CCG 0, coincidiendo con lo que pensé que las cosas funcionen. Pero N3225 dice que std::bind(f, a1) devolverán una envoltura llamada que cuando se le llama por wrapper() llamará INVOKE(f, v1), donde v1 será a (el argumento pasé en, en otras palabras, usando binds 's de parámetros de entrada, que es un parámetro de reenvío perfecto, std::forward<A1>(a1)) .

INVOKE(f, a) se define en 20.8.2 a f(a). Por lo tanto, esto define que la llamada al contenedor de llamadas devuelto pasa el argumento original. ¿Qué me estoy perdiendo?

+0

http://stackoverflow.com/questions/4327474/does-perfect-forwarding-in-c0x-make-reference-wrapper-deprecated (No es un duplicado pero está relacionado, si obtendremos un "reenvío perfecto" en C + + 0x) – CashCow

Respuesta

2

Guau, esto es confuso más allá de toda creencia. Define v1 como tid y lo siguiente (ti es el parámetro de enlace de reenvío perfecto i-ésimo, y TiD es el tipo decaído de ese parámetro, es decir, una matriz se convierte en un puntero, etc.).

tid es un valor-I de tipo TiD construido a partir de std::forward<Ti>(ti)

bien, lo he dicho, esto es tidstd::forward<Ti>(ti) y es un valor izquierdo! Pero esto no es lo que realmente significa decir. Significa

tid es un lvalue de tipo TiD que hace referencia a un objeto construido a partir de std::forward<Ti>(ti)

Tiene mucho más sentido ahora. Porque, ¿qué pasa si std::forward<Ti>(ti) es realmente un valor r? El "lvalue ... construido a partir de ..." significa que creamos un nuevo objeto a partir de "..." y hacemos que el valor l se refiera a él.

3

Imprime un 0 en la actualidad porque en el punto que llama a std :: bind no sabe que quiere pasar una referencia. No mira la firma de la función para ver qué tipos de parámetros toma y ajustar en consecuencia.

para que funcione correctamente llamar

void f(int &a) { a = 1; } 

int main() { 
    int a = 0; 
    std::bind(f, std::ref(a))(); 
    std::cout << a << std::endl; 
} 

C++ 0x está sugiriendo "unión perfecta", pero hay un gran peligro de este, lo que podría mal romper el código existente en silencio que se basa en el comportamiento actual. Aquí hay un ejemplo muy simple.

void myCallback(const std::string& str, int i); 

function< void(int) > makeCallback(const std::string & str) 
{ 
    return bind(myCallback, str, _1); 
} 

En la actualidad se puede confiar en la copia se unen la cadena que se pasa en la str y por lo tanto el hecho de que será válida viene la invocación de la devolución de llamada.

Si utiliza "inteligentemente" "encuadernación perfecta" para almacenarlo como referencia, rompería situaciones como esta.

+2

El cartel original claramente indica que sí sabe usar 'std :: ref'. Pidió que se interpretara la especificación, * no * cómo hacer que el código se imprima 1, por lo que esta no es una respuesta. –

+0

Ahora he respondido por qué no forzar explícitamente std :: ref podría ser muy peligroso. No estoy seguro de que responda directamente la pregunta y realmente no entiendo todas estas cosas ... – CashCow

5

Dice lo siguiente debería imprimir 1

No, no dice eso.

Si uno quiere consultar el argumento pasado, uno necesita usar std :: ref, ¿verdad?

Sí.

Pero N3225 dice que std :: bind (f, a1) devolverá una envoltura llamada que cuando se le llama por la envoltura() llamará Invoke (f, v1), donde v1 será una (el argumento pasé en otras palabras, utilizando el parámetro entrante de binds que es un parámetro de reenvío perfecto, std :: forward (a1)).

Ahí es donde te equivocas. Los "argumentos encuadernados" que pasó en la llamada de enlace se almacenan en forma de objetos recién creados del tipo TiD, cada uno de ellos construido de forward<Ti>(ti). Esto se hace razonablemente claro al decir "tid es un lvalue de tipo TiD construido desde std::forward<Ti>(ti)". Debido al tratamiento especial de los envoltorios de referencia, existe una "capa de transformación" adicional. Consulte 20.8.10.1.2/10 que explica cómo vi y Vi se relacionan con tid y TiD.

+0

Para mí, decir que un lvalue de 'TiD' se construye a partir de' adelante (ti) 'significa que' adelante (ti) 'será un lvalue o que se convertirá en' TiD & 'como en' (TiD &) forward (ti) 'para que sea un valor l. No hay forma de que pueda leerlo diciendo que creará un nuevo objeto al que se refiere el valor l. Porque en tal caso, el lvalue se construiría nombrando ese nuevo objeto. OMI es una manera muy confusa de decirlo. –

+0

"construcción" implica "objeto". En '(TiD &) forward (ti)' no tiene lugar la construcción de objetos. Una referencia de lvalue no es un tipo de objeto. – sellibitze

+0

"construido" puede significar otras cosas además de crear objetos. Compare con el "programa C++ construido de acuerdo con las reglas de sintaxis [...]" (definición de "bien formado"). "construir" usado junto con "construir un lvalue" no significa crear un objeto. Construir un lvalue solo puede significar lo que dice, no otra cosa. El siguiente código construye un valor l a partir del identificador 'x': 'int main() {int x = 0; X; } ' En el siguiente código, * no se construye lvalue * 'int main() {int x = 0; } ' –

Cuestiones relacionadas