2008-09-09 10 views
6

Un amigo y yo estábamos discutiendo las plantillas C++. Me preguntó qué debería hacer esto:Ambigüedad de la plantilla C++

#include <iostream> 

template <bool> 
struct A { 
    A(bool) { std::cout << "bool\n"; } 
    A(void*) { std::cout << "void*\n"; } 
}; 

int main() { 
    A<true> *d = 0; 
    const int b = 2; 
    const int c = 1; 
    new A<b> (c) > (d); 
} 

La última línea en main tiene dos análisis razonables. ¿Es 'b' el argumento de la plantilla o es b > (c) el argumento de la plantilla?

Aunque, es trivial compilar esto, y ver lo que obtenemos, nos preguntamos ¿qué resuelve la ambigüedad?

Respuesta

5

AFAIK se compilaría como new A<b>(c) > d. Esta es la única forma razonable de analizarlo en mi humilde opinión. Si el analizador no puede asumir bajo circunstancias normales a> finalizar un argumento de plantilla, eso generaría mucha más ambigüedad. Si usted quiere la otra manera, debería haber escrito:

new A<(b > c)>(d); 
0

La codicia del lexer es probablemente el factor determinante en la ausencia de paréntesis para hacerlo explícito. Supongo que el lexer no es codicioso.

+0

Pero no es el léxico completamente inequívoca? –

+0

Error en la ambigüedad, entonces sí. Ser codicioso o no codicioso no te hace ambiguo. La codicia es en realidad una forma de resolver la ambigüedad. En este caso, también entres en la interacción entre el analizador y el analizador, por lo que tal vez el analizador sintáctico no sea codicioso. –

+0

La gramática puede ser ambigua, pero el lexer no lo es en absoluto. El espaciado hace que el lector esté completamente bien definido. –

3

estándar de C++ define que si por un nombre de plantilla seguido de un <, la < es siempre el comienzo de la lista de argumentos de plantilla y el primer no -nested > se toma como el final de la lista de argumentos de la plantilla.

Si pretendía que el resultado del operador > fuera el argumento de la plantilla, debería encerrar la expresión entre paréntesis. No necesita paréntesis si el argumento era parte de una static_cast<> u otra expresión de plantilla.

6

Según lo indicado por Leon & Lee, 14.2/3 (C++ '03) define explícitamente este comportamiento.

C++ '0x se agrega a la diversión con una regla similar que se aplica a >>. El concepto básico es que al analizar un argumento-lista de plantillas de un no anidada >> serán tratados como dos distintas >> fichas y no el derecho operador de desplazamiento:

template <bool> 
struct A { 
    A(bool); 
    A(void*); 
}; 

template <typename T> 
class C 
{ 
public: 
    C (int); 
}; 

int main() { 
    A<true> *d = 0; 
    const int b = 2; 
    const int c = 1; 
    new C <A<b>> (c) > (d); // #1 
    new C <A<b> > (c) > (d); // #2 
} 

'# 1' y '# 2' son equivalentes en lo anterior.

Esto, por supuesto, las correcciones que molestia de tener que añadir espacios en especializaciones anidados:

C<A<false>> c; // Parse error in C++ '98, '03 due to "right shift operator" 
+0

He oído hablar de esto antes. Algunos compiladores han saltado el arma al permitir 'x >' de todos modos. Pero me pregunto: ¿está realmente definido en términos de lexing sensible al contexto, o por una regla de gramática como TemplateId '<' TemplateId '<' TemplateArgument '>>' dada por derecho propio? – Stewart

+1

@Stewart: es el primero (lexing sensible al contexto). Para resumir, 14.2/3 dice que al analizar una plantilla-argumento-lista, el primer >> no anidado se trata como dos tokens separados. Puede leer el documento actual para obtener más información: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html –

Cuestiones relacionadas