2012-09-28 10 views
9

Ich versuche, eine ANSI C++ for_each-Anweisung zu verwenden, um die Elemente eines Standardvektors zu durchlaufen und zu drucken. Es funktioniert, wenn ich den for_each Aufruf eine nicht überladene Funktion habe, aber einen Compiler-Fehler ergibt, wenn ich es eine überladene Funktion nennen lasse.g ++ - Compilerfehler: Vorlageparameter '_Func' kann nicht abgeleitet werden

Hier ist ein minimal-Testprogramm zu zeigen, wo der Compiler-Fehler auftritt:

#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'; 
} 

ich die 12.2 in openSUSE kompiliert werden:

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

Die vollständige Fehlermeldung lautet:

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’ 

Ich sehe keine Suchergebnisse für diesen bestimmten Fehler auf Stack Overflow oder im Web ge nerally. Jede Hilfe wäre willkommen.

+0

http://ideone.com/nqL3x – BoBTFish

Antwort

6

Ein Name bezieht sich auf einen Überladungssatz. Sie müssen angeben müssen, welche Sie überlasten:

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

Ein weiterer Ansatz ist es, ein polymorph aufrufbaren Funktionsobjekt als Helfer zu verwenden:

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

Vielen Dank für eine schnelle, klare, funktional korrekte Antwort. Es löst den Fehler. –

+0

@Matt: Auf SO, wenn eine Antwort Ihr Problem gelöst hat, bitte * akzeptieren * diese Antwort. Sie tun dies, indem Sie auf das umrissene Häkchen neben der Antwort klicken. :) Wenn mehrere Antworten Ihr Problem gelöst haben, dann wählen Sie einen aus. – Xeo

4

std::for_each Erklärung wie folgt aussieht:

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

Wie Sie sehen können, dauert es alles geben Sie es als dritten Parameter an. Es gibt keine Einschränkung, dass es sich um einen aufrufbaren Typ einer bestimmten Signatur oder überhaupt um einen aufrufbaren Typ handeln muss.

Im Umgang mit überladenen Funktionen sind sie mehrdeutig, es sei denn, Sie geben ihnen Kontext, um den richtigen zu wählen. In einem Aufruf einer überladenen Funktion sind dieser Kontext die Argumente, die Sie übergeben. Wenn Sie jedoch einen Zeiger benötigen, können Sie Argumente nicht als Kontext verwenden, und der Parameter for_each zählt auch nicht als Kontext, da er alles erfordert.

Als Beispiel, wo ein Funktionsparameter ein gültiger Kontext sein kann, die richtige Überlastung zu wählen, sehen folgendermaßen aus:

// 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 

Wie Sie sehen können, Ihnen einen klaren Zusammenhang geben hier, dass der Compiler die Auswahl hilft richtige Überladung und nicht mehrdeutig. 's Parameter tun nicht geben einen solchen Kontext, da sie alles nehmen.

Es gibt zwei Lösungen:

  • manuell den Kontext zu machen, entweder durch
    • nach rechts Funktionszeigertyp Gießen oder
    • eine Zwischenvariable des richtigen Typs verwendet und Weitergabe dass
  • Verwenden Sie eine nicht überladene Funktion, die zu einer überladenen (wie Sie mit f)

Beachten Sie, dass in C++ 11, können Sie auch eine Lambda für die zweite Option verwenden:

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

einige Noten auf dem Code:

  • (struct S){'a', 1} ist eine Verbindung, Literal und nicht Standard C++
  • Sie brauchen nicht struct S in C++, nur S genügt
+0

Viele gute Hintergrund dort. Die Hinweise zu meinem Code werden ebenfalls geschätzt. Ich frage mich, warum die zusammengesetzten Literale die Fehlerprüfung von g ++ - 4.7 überstanden haben. Ich habe die -ansi-Flagge benutzt. –

+0

@user: Verwenden Sie '-pedantic',' -ansi' impliziert C99 IIRC, und GCC unterstützt das standardmäßig in C++. – Xeo

Verwandte Themen