ternario operador ejemplo c++ language-lawyer compiler-bug name-lookup dependent-name

c++ - ejemplo - Tipo de retorno del operador condicional y búsqueda en dos fases



operador ternario ejemplo c++ (3)

Creo que gcc (y visual studio, por cierto) tienen razón en este caso.

n4582 , §14.6.2.2

Excepto como se describe a continuación, una expresión depende del tipo si alguna subexpresión depende del tipo.

En T{} ? d : d T{} ? d : d , hay 3 sub expresiones:

  • T{} , obviamente depende del tipo
  • d (2 veces), no depende del tipo

Como hay una subexpresión dependiente del tipo y el operador ternario no figura en la lista de excepciones en §14.6.2.2, se considera dependiente del tipo.

Considere el siguiente fragmento:

struct Base { }; struct Derived : Base { }; void f(Base &) { std::cout << "f(Base&)/n"; } template <class T = int> void g() { Derived d; f(T{} ? d : d); // 1 } void f(Derived &) { std::cout << "f(Derived&)/n"; } int main() { g(); }

En este caso, considero que la llamada a la función f en // 1 debe buscarse en la fase uno, ya que el tipo de su argumento es Derived& inequívocamente, y así se resuelve en f(Base&) que es el único en el alcance.

Clang 3.8.0 está de acuerdo conmigo , pero GCC 6.1.0 no lo hace , y difiere la búsqueda de f hasta la fase dos, donde se recoge f(Derived&) .

¿Qué compilador tiene razón?


De acuerdo con el borrador c ++ ( n4582 ) §14.7.1.5:

A menos que una especialización de plantilla de función haya sido explícitamente instanciada o explícitamente especializada, la especialización de plantilla de función se instanciará implícitamente cuando se haga referencia a la especialización en un contexto que requiera la existencia de una definición de función. A menos que una llamada sea a una plantilla de función especialización explícita oa una función miembro de una plantilla de clase explícitamente especializada, un argumento predeterminado para una plantilla de función o una función miembro de una plantilla de clase se instanciará implícitamente cuando se llame a la función en un contexto que requiera el valor del argumento predeterminado.

Diría que gcc es más correcto acerca de esto.

Si, por ejemplo, creas una versión especializada de void g() , obtienes que ambos compiladores hagan lo mismo .


Usando la última versión del estándar C ++ Actualmente n4582 .

En la sección 14.6 (p10) dice que el nombre está vinculado en el punto de declaración si el nombre no depende de un parámetro de plantilla. Si depende de un parámetro de plantilla, esto se define en la sección 14.6.2.

La Sección 14.6.2.2 continúa diciendo que una expresión depende del tipo si alguna subexpresión depende del tipo.

Ahora, dado que la llamada a f() depende de su parámetro. Miras el tipo de parámetro para ver si depende del tipo. El parámetro es False<T>::value ? d : d False<T>::value ? d : d . Aquí el primer condicional depende del tipo T

Por lo tanto, concluimos que la llamada está vinculada en el punto de creación de instancias, no de declaración. Y, por lo tanto, debería vincularse a: void f(Derived &) { std::cout << "f(Derived&)/n"; } void f(Derived &) { std::cout << "f(Derived&)/n"; }

Por lo tanto, g ++ tiene la implementación más precisa.

14.6 Resolución del nombre [temp.res]

Para 10:

Si un nombre no depende de un parámetro de plantilla (como se define en 14.6.2) , una declaración (o conjunto de declaraciones) para ese nombre estará dentro del alcance en el punto donde el nombre aparece en la definición de la plantilla; el nombre está vinculado a la declaración (o declaraciones) encontradas en ese punto y este enlace no se ve afectado por las declaraciones que son visibles en el momento de la creación de instancias.

14.6.2.2 Expresiones dependientes del tipo [temp.dep.expr]

Excepto como se describe a continuación, una expresión depende del tipo si alguna subexpresión depende del tipo .