2011-03-02 22 views
34

Puede alguien ayudarme a entender el siguiente códigoreferencia pase a la matriz en C++

#include <iostream> 

void foo(const char * c) 
{ 
    std::cout << "const char *" << std::endl; 
} 

template <size_t N> 
void foo(const char (&t) [N]) 
{ 
    std::cout << "array ref" << std::endl; 
    std::cout << sizeof(t) << std::endl; 
} 

int main() 
{ 
    const char t[34] = {'1'}; 
    foo(t); 

    char d[34] = {'1'}; 
    foo(d); 
} 

La salida es

const char * 
array ref 
34 

¿Por qué la primera foo llama a la versión const char *? ¿Cómo puedo hacer que llame a la versión de referencia?

+0

La salida es 'const char *, const char *' para mí en msvc – Marlon

+0

La salida es como él dice, 'const char *, matriz ref, 34' con' gcc-4.3.4' (http: // ideone.com/ejyCS). – James

+1

@ user511274 - Pregunta interesante :) –

Respuesta

15

Conversión de const char[N] a const char* se considera una "coincidencia exacta" (para hacer más fácil literales, principalmente), y entre los dos exacta coincide con una plantilla de función no tiene prioridad.

Puede usar enable_if y is_array para forzarlo a hacer lo que desee.


Una forma desordenada a la fuerza podría ser:

#include <iostream> 

template <typename T> 
void foo(const T* c) 
{ 
    std::cout << "const T*" << std::endl; 
} 

template <typename T, size_t N> 
void foo(const T (&t) [N]) 
{ 
    std::cout << "array ref" << std::endl; 
} 

int main() 
{ 
    const char t[34] = {'1'}; 
    foo(t); 

    char d[34] = {'1'}; 
    foo(d); 
} 

/* 
array ref 
array ref 
*/ 

que darse cuenta de que el PO tenía char no un genérico T, pero no obstante esto demuestra que el problema estaba en una sobrecarga de ser una plantilla y no el otro

+1

¿Cómo funciona? explique que la segunda versión de 'foo()' se llama la segunda vez? – fouronnes

+1

@otibom: La conversión de 'char [N]' no tiene la misma regla. 'char [N]' to 'const char *' no es una coincidencia exacta: es convertible, pero la función de la plantilla se combina más fácilmente. –

+0

@otibom La primera versión no es const, pero la versión de la plantilla tiene un puntero de matriz const. – Lundin

5

Miremos este ejemplo modificado sin plantilla.

void foo(const char * c) 
{ 
    std::cout << "const char *" << std::endl; 
} 

void foo(const char (&t) [34]) 
{ 
    std::cout << "const char (&) [34]" << std::endl; 
} 

int main() 
{ 
    const char t[34] = {'1'}; 
    foo(t); 
} 

Mi compilador dice llamada de sobrecarga foo es ambigua. Esto se debe a que las conversiones de matriz a puntero se consideran una secuencia de conversión "Exacta" y no son mejores que la secuencia de conversión nula para resolución de sobrecarga (sección estándar 13.3.3.1.1.)

En el código original, el parámetro de plantilla N se puede deducir como 34, pero se considera que ambas plantillas foo(const char*) y foo<34>(const char (&)[34]) tienen una resolución de sobrecarga. Como ninguna de las reglas de conversión es mejor que la otra, la función sin plantilla vence a la función de la plantilla.

Reparar cosas parece complicado. Parece que la plantilla is_array del encabezado <type_traits> (de C++ 0x si es posible o Boost si no) podría ayudar.

+0

De hecho, puede usar enable_if e is_array para obtener lo que desea. –

+0

@Tomalak: ¿Hay alguna forma sencilla de utilizar C++ 03 y Boost? Iba a dar un ejemplo, y luego me di cuenta de que necesitaba absolutamente las referencias rvalue, que no son parte del lenguaje en la etiqueta de la pregunta. – aschepler

+0

@aschepler: Actualicé mi respuesta con un enfoque que implicaba nivelar el campo de juego. –

1

Esto parece ser diferente para varios compiladores.

Tanto Mircosoft como Borland usan la versión const char *, mientras que GNU proporciona la salida que describió.

Aquí hay un fragmento del C++ estándar:

14.8.2.1 Deducción de argumentos de plantilla a partir de una llamada a la función [temp.deduct.call]

Plantilla argumento deducción se realiza mediante la comparación de cada la plantilla de función tipo de parámetro (llámala P) con el tipo del argumento correspondiente de la llamada (llámala A) como se describe a continuación .

Si P no es un tipo de referencia:

- Si A es un tipo de matriz, el tipo de puntero producida por la conversión estándar-array-a puntero (4.2) se utiliza en lugar de A para tipo deducción; lo contrario,

- Si A es un tipo de función, el tipo de puntero producido por el conversión función-a-puntero estándar (4.3) se utiliza en lugar de un para el tipo de deducción; de lo contrario,

- Si A es un tipo cv-calificado, los cv-calificadores de nivel superior del tipo de A se ignoran para la deducción de tipo.

Si P es un tipo cv-calificado, los mejores cv-calificadores de nivel de tipo P son ignorados para la deducción de tipo. Si P es un tipo de referencia, el tipo a que se refiere por P se utiliza para el tipo de deducción

el compilador construir una lista de A de la siguiente manera:

Argument:  t     d 
A:   char const[34]  char[34] 

Y lista de parámetros P:

Parameter:  c     t 
P:   char const*  char const& t[N] 

De forma predeterminada, el compilador debe elegir los parámetros no referenciados. GNU está equivocado la segunda vez por alguna razón.

+0

No, no lo es. Te estás olvidando de las plantillas que no se prefieren. Si MS y Borland dan salida diferente, entonces no son compatibles. La deducción del tipo de plantilla es completamente irrelevante. –

+0

El argumento de deducción de la plantilla no es el problema. La resolución de sobrecarga es – aschepler

Cuestiones relacionadas