2012-09-28 14 views
9

Estoy tratando de usar una instrucción ANSI C++ for_each para iterar e imprimir los elementos de un vector estándar. Funciona si tengo la llamada for_each una función no sobrecargada, pero produce un error de compilación si la llamo a una función sobrecargada.error de compilador de g ++: no se pudo deducir el parámetro de plantilla '_Funct'

Aquí es un programa de prueba mínima para mostrar dónde se produce el error del compilador:

#include <algorithm> 
#include <iostream> 
#include <vector> 

struct S { 
    char c; 
    int i; 
}; 
std::vector<S> v; 

void print_struct(int idx); 
void print_struct(const struct S& s); 

// f: a non-overloaded version of the preceding function. 
void f(const struct S& s); 

int main() 
{ 
    v.push_back((struct S){'a', 1}); 
    v.push_back((struct S){'b', 2}); 
    v.push_back((struct S){'c', 3}); 

    for (unsigned int i = 0; i < v.size(); ++i) 
     print_struct(i); 

    /* ERROR! */ 
    std::for_each(v.begin(), v.end(), print_struct); 

    /* WORKAROUND: */ 
    std::for_each(v.begin(), v.end(), f); 

    return 0; 
} 

// print_struct: Print a struct by its index in vector v. 
void print_struct(int idx) 
{ 
    std::cout << v[idx].c << ',' << v[idx].i << '\n'; 
} 

// print_struct: Print a struct by reference. 
void print_struct(const struct S& s) 
{ 
    std::cout << s.c << ',' << s.i << '\n'; 
} 

// f: a non-overloaded version of the preceding function. 
void f(const struct S& s) 
{ 
    std::cout << s.c << ',' << s.i << '\n'; 
} 

Compilé esto en openSUSE 12.2 usando:

g++-4.7 -ansi -Wall for_each.cpp -o for_each 

El mensaje de error completo es:

for_each.cpp: In function ‘int main()’: 
for_each.cpp:31:48: error: no matching function for call to ‘for_each(std::vector<S>::iterator, std::vector<S>::iterator, <unresolved overloaded function type>)’ 
for_each.cpp:31:48: note: candidate is: 
In file included from /usr/include/c++/4.7/algorithm:63:0, 
       from for_each.cpp:5: 
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note: template<class _IIter, class _Funct> _Funct std::for_each(_IIter, _IIter, _Funct) 
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note: template argument deduction/substitution failed: 
for_each.cpp:31:48: note: couldn't deduce template parameter ‘_Funct’ 

No veo ningún resultado de búsqueda para este error en particular en Stack Overflow, o en la web ge nerally. Cualquier ayuda sería apreciada.

+0

http://ideone.com/nqL3x – BoBTFish

Respuesta

6

Un nombre hace referencia a un conjunto de sobrecarga. Tendrá que especificar qué sobrecarga desea:

std::for_each(v.begin(), v.end(), (void (&)(S const&)) print_struct); 

Otro enfoque es utilizar una función polimórfica objeto exigible como ayudante:

struct PrintStruct 
{ 
    template <typename T> void operator()(T const& v) const 
     { return print_struct(v); } 
}; 

int main() 
{ 
    PrintStruct helper; 

    std::vector<S> sv; 
    std::vector<int> iv; 

    // helper works for both: 
    std::for_each(sv.begin(), sv.end(), helper); 
    std::for_each(iv.begin(), iv.end(), helper); 
+0

Gracias por una respuesta rápida, clara y funcionalmente correcta. Resuelve el error. –

+0

@Matt: en SO, si alguna respuesta resolvió su problema, * acepte * esa respuesta. Para hacer eso, haga clic en la marca delineada al lado de la respuesta. :) Si varias respuestas resolvieron tu problema, bueno, solo elige uno. – Xeo

4

std::for_each declaración es el siguiente:

template<class InputIter, class Func> 
void for_each(InputIter first, InputIter last, Func func); 

Como puede ver, se necesita cualquier cosa lo das como el tercer parámetro. No hay ninguna restricción de que tenga que ser un tipo invocable de una determinada firma o un tipo invocable.

Cuando se trata de funciones sobrecargadas, son inherentemente ambiguo menos que les dé un poco de contexto para seleccionar el más adecuado. En una llamada a una función sobrecargada, este contexto son los argumentos que pasa. Sin embargo, cuando necesita un puntero, no puede usar argumentos como contexto, y el parámetro for_each tampoco cuenta como contexto, ya que toma cualquier cosa.

Como un ejemplo de que un parámetro de función puede ser un contexto válido para seleccionar la sobrecarga de la derecha, ver esto:

// our overloads 
void f(int){} 
void f(double){} 

typedef void (*funcptr_type)(int); 
void g(funcptr_type){} 

// ... 
g(&f); // will select 'void f(int)' overload, since that's 
     // the only valid one given 'g's parameter 

Como se puede ver, le dan un contexto claro aquí que ayuda a que el compilador de seleccionar el sobrecargar correctamente y no tenerlo ambiguo. Los parámetros std::for_each hacen no dan ese contexto, ya que toman cualquier cosa.

hay dos soluciones:

  • proporcionan manualmente el contexto, ya sea por
    • fundición a la función derecha tipo de puntero, o
    • utilizando una variable intermedia del tipo correcto y que pasan que
  • utilizan una función no sobrecargada que se distribuye a una sobrecargada (como lo hizo con f)

Tenga en cuenta que en C++ 11, también se puede utilizar un lambda por la segunda opción:

std::for_each(v.begin(), v.end(), [](const S& s){ print_struct(s); }); 

algunas notas en el código:

  • (struct S){'a', 1} es un compuesto literal y no en C++ estándar
  • que no es necesario struct S en C++, solamente basta S
+0

Hay muchos buenos antecedentes allí. Las notas en mi código también son apreciadas. Me pregunto por qué los literales compuestos pasaron la comprobación de errores de g ++ - 4.7. Usé la bandera -ansi. –

+0

@user: Usar '-pedantic',' -ansi' implica C99 IIRC, y GCC lo admite por defecto en C++. – Xeo

Cuestiones relacionadas