2009-12-07 12 views
5

Tengo curiosidad acerca de algunas de las nuevas características de C++ 0x. En particular, range-based for loops y initializer lists. Ambas funciones requieren una clase definida por el usuario para funcionar correctamente.C++ 0x, ganchos del compilador e idiomas codificados características

Me encontré con this post, y aunque la respuesta principal fue útil. No sé si es completamente correcto (Probablemente estoy completamente equivocado, vea el 3er comentario en la primera respuesta). De acuerdo con la current specifications para las listas de inicializador, la cabecera define un tipo:

template<class E> class initializer_list { 
public: 
    initializer_list(); 

    size_t size() const; // number of elements 
    const E* begin() const; // first element 
    const E* end() const; // one past the last element 
}; 

Esto se puede ver en las especificaciones, simplemente Ctrl + F 'clase initializer_list'.

Para que = {1,2,3} a ser moldeado de manera implícita en la clase initializer_list, el compilador tiene que tener un cierto conocimiento de la relación entre {} y initializer_list. No hay ningún constructor que reciba nada, por lo que la initializer_list hasta donde puedo decir es un contenedor que se vincula con lo que el compilador realmente está generando.

Es lo mismo con el bucle for(:), que también requiere un tipo definido por el usuario para trabajar (aunque de acuerdo a las características, actualizados al no requiere ningún código para matrices y listas de inicializador. Pero las listas de inicializador requiere <initializer_list>, así que es una requisito de código definido por el usuario por proxy).

¿Estoy entendiendo completamente cómo funciona esto aquí? No me equivoco al pensar que estas nuevas características realmente dependen en gran medida del código de usuario. Se siente como si las funciones estuvieran medio cocidas, y en lugar de compilar toda la función en el compilador, está a medio hacer por el compilador y medio hecho en includes. ¿Cuál es el motivo de esto?

Edit: Escribí "confío mucho en el código del compilador", y no "confío mucho en el código de usuario". Lo cual creo que eliminó por completo mi pregunta. Mi confusión no se trata de nuevas características integradas en el compilador, sino de elementos integrados en el compilador que se basan en el código de usuario.

Respuesta

3

no me equivoco al pensar que estas nuevas características hacen de hecho dependen muy fuertemente de código de compilador

Ellos dependen extremadamente del compilador. Ya sea que necesite incluir un encabezado o no, el hecho es que, en ambos casos, la sintaxis sería un error de análisis con los compiladores de hoy. El for (:) no encaja en el día de hoy estándar, donde la construcción sólo se permite es for(;;)

Se siente como si las funciones son a medias, y en lugar de la construcción de toda la función en el compilador, que está siendo medio-hecho por el compilador y medio hecho en includes. ¿Cuál es el motivo de esto?

El soporte debe implementarse en el compilador, pero debe incluir un encabezado de sistema para que funcione. Esto puede servir para un par de propósitos, en el caso de las listas de inicialización, trae el tipo (interfaz al soporte del compilador) dentro del alcance del usuario para que pueda tener una forma de usarlo (piense cómo va_args están en C). En el caso del basado en el rango para (que es solo azúcar sintáctico), necesitas llevar el alcance al alcance para que el compilador pueda realizar su magia. Tenga en cuenta que la norma define for (for-range-declaration : expression) statement como equivalente a ([6.5.4]/1 en el proyecto):

{ 
    auto && __range = (expression); 
    for (auto __begin = std::Range<_RangeT>::begin(__range), 
     __end = std::Range<_RangeT>::end(__range); 
     __begin != __end; 
     ++__begin) { 
     for-range-declaration = *__begin; 
     statement 
    } 
} 

Si desea utilizarlo solamente con matrices y contenedores STL que podrían ser implementadas sin el concepto Range (no en el sentido C++ 0x), pero si desea extender la sintaxis en clases definidas por el usuario (sus propios contenedores), el compilador puede depender fácilmente de la plantilla existente Range (con su posible especialización). El mecanismo de dependencia de una plantilla que se está definiendo es equivalente a requerir una interfaz estática en el contenedor.

La mayoría de los otros idiomas han ido en la dirección de requerir una interfaz regular (por ejemplo, contener, ...) y usar polimorfismo de tiempo de ejecución en eso. Si eso se hiciera en C++, todo el STL tendría que pasar por una refactorización importante ya que los contenedores STL no comparten una base o interfaz común, y no están preparados para ser utilizados polimórficamente.

En caso de existir, el estándar actual no será con menos de antes de que se apague.

+0

Entiendo el requisito para el constructo for (:), sin el compilador que requiere una iteración diferente del contenedor sería imposible. Pero el for (:) no exige que un tipo funcione, solo si tiene la intención de ampliarlo. Lo cual para mí es similar al nuevo mecanismo de sobrecarga de ubicación. Sin embargo, todavía no veo el motivo de la lista initializer_type. = {1,2,3,4,5} a una matriz literal, pasada a un operador vacío {} (int arr [], 5); parece más limpio y no requiere sistemas de inclusión. Si el usuario quiere ponerlo en un contenedor, puede hacerlo en esa función. – Dave

+0

Al igual que mencionó va_args, las funciones y macros se basan en una característica de compilación. No obtendrá un error de compilación por no incluir va_args, podría escribir el suyo. Del mismo modo, C++ 0x []() {} lambdas no requieren std :: function, de nuevo podrías escribir el tuyo propio. {}, por otro lado, exige la existencia de un nombre de clase específico, que se bloqueará si no existe. Y no debería ser forzado, supongo que estoy simplemente 'nerd furioso', pero mucho de lo que estoy viendo en el estándar C++ 0x parece cada vez más extraño por minuto. – Dave

+0

Es un poco más complicado que eso. El compilador y los implementadores de STL no necesitan ser lo mismo. Microsoft usa la implementación Dinkunware STL, pero puede usar STLPort o cualquier otro. Las clases de STL no están definidas y definidas al 100%, y un implementador de STL puede decidir agregar nuevos parámetros de plantilla siempre que tengan un valor predeterminado y puedan usarse en el código de usuario que depende y usa solo los que están en el estándar. Esto significa que el compilador realmente no puede hacer coincidir fácilmente los contenedores STL. –

1

Es simplemente azúcar sintáctica. El compilador expandirá las construcciones sintácticas dadas en expresiones C++ equivalentes que hacen referencia directamente a los nombres estándar de tipos/símbolos.

Este no es el único acoplamiento fuerte que los compiladores modernos de C++ tienen entre su lenguaje y el "mundo exterior". Por ejemplo, extern "C" es un truco de lenguaje para acomodar el modelo de vinculación de C. Las formas orientadas al lenguaje de declarar el almacenamiento local de subprocesos dependen implícitamente de muchos hackers de RTL para funcionar.

O mira C. ¿Cómo se accede a los argumentos pasados ​​a través de ...? Debe confiar en la biblioteca estándar; pero eso usa magia que tiene una dependencia muy fuerte de cómo exactamente el compilador C establece marcos de pila.

UPDATE:

En todo caso, el enfoque C++ ha tomado aquí es más en el espíritu de C++ que la alternativa - que sería añadir un intrínseca colección o rango tipo, al horno en la idioma. En cambio, se está haciendo a través de un tipo de rango definido por el vendedor. Realmente no lo veo tan diferente a los argumentos variados, que son igualmente inútiles sin las macros de acceso definidas por el vendedor.

+0

... y extern "C" son características del lenguaje, sin embargo, no necesita escribir la clase que se expande ... en argumentos, solo usa ... para indicar lo que el compilador debería hacer. {} por otro lado requiere una clase llamada initializer_list, si eso no existe no funcionará, puedes compilar la función (int a, ...) sin incluir nada. – Dave

+0

Dave, thread_local no funcionará sin agallas en el RTL tampoco, ni soporte de excepción-envío. Creo que acabas de vivir una vida protegida :) - el compilador está haciendo más trabajo con la cooperación de RTL del que has tenido conocimiento, y lo has dado por sentado. Ahora viene otra característica ('{}') que necesita soporte RTL ('std :: initializer_list') ¡y te sorprendes! –

+0

No, por supuesto, pero el identificador de almacenamiento thread_local es solo eso, un identificador que especifica qué hacer. No tengo ningún problema con el compilador haciendo un trabajo de fondo. Su ejemplo de ... por ejemplo, la ... característica de idioma NO NECESITA va_list para que funcione. va_list se basa en una característica del compilador, demonios podrías escribirlo tú mismo. std :: initializer por otro lado requiere código para existir, puede compilar function (int, ...) sin inclusiones. Y, por supuesto, ¿cómo {} necesita soporte RTL? – Dave

Cuestiones relacionadas