Forum: PC-Programmierung [C++] Problem mit Template-Funktion und Überladung


von Slippin J. (gustavo_f)


Lesenswert?

Hallo,

ich versuche schon seit Stunden folgendes Problem zu lösen. Hier erstmal 
das Minimalbeispiel in C++:
1
#include <iostream>
2
#include <string>
3
#include <thread>
4
5
template <typename T>
6
class Queue
7
{
8
 public:
9
  T pop() {}
10
  void push(const T& item) {}
11
};
12
13
void produce(Queue<int>& q) {
14
    q.push(1234);
15
}
16
17
// wenn diese Funktion auskommentiert ist, funktioniert es
18
void produce(Queue<std::string>& q) {
19
   q.push("Hallo");
20
}
21
22
int main()
23
{
24
  Queue<int> my_queue;
25
  auto f = std::bind(&produce, my_queue); // <<<  FEHLER
26
}

Übersetzt habe ich das mit:
1
g++ -Wall -Wextra -pedantic-errors -std=c++0x -O2 -pthread producer_consumer1.cpp -o producer_consumer1

Ich habe eine Template-Klasse Queue geschrieben. Ich erzeuge eine Queue, 
welche erstmal nur ints aufnehmen soll. Eine überladene Funktion produce 
bekommt diese Queue und füllt diese mit einem Int-Wert. Soweit 
funktioniert das erstmal.
Habe ich allerdings eine zweite Funktion produce (Zeile 18), welche eine 
String-Queue übergeben bekommt, erhalte ich die Fehlermeldung:
1
producer_consumer1.cpp: In function ‘int main()’:
2
producer_consumer1.cpp:25:40: error: no matching function for call to ‘bind(<unresolved overloaded function type>, Queue<int>&)’
3
   auto f = std::bind(&produce, my_queue); // <<<  FEHLER

Kann mir jemand weiterhelfen?

von Sven S. (Gast)


Lesenswert?

Das hat damit zu tun, das dein bind ambigous ist. Um dies zu beheben, 
musst du das ganze casten, schau mal hier nach:

http://stackoverflow.com/questions/4159487/stdbind-overload-resolution
http://stackoverflow.com/questions/1915880/boostbind-boostfunction-pointers-to-overloaded-or-templated-member-functio

Lass dich nicht von den Klassen verwirren, das gilt auch für funktionen 
im globaln namespace.

Ich denke, bei dir müsste das so lauten:
auto f = std::bind(static_cast<void *( Queue<int>& )>(&produce), 
my_queue);

von The D. (thedaz)


Lesenswert?

1
std::bind(&produce<std::string>, my_queue)

von derElf (Gast)


Lesenswert?

Ich kann nicht wirklich C++, aber kann es sein, dass wenn du das 
Template als Queue<int> mit dem Typ "int" initialisierst, du dann den 
Falschen Typ in void push(const T& item) übergibst?
Weil T wird dann ja zu int nicht std::string

von derElf (Gast)


Lesenswert?

Ok, anscheinend ist meine Idee ziemlich falsch :)

von The D. (thedaz)


Lesenswert?

The D. schrieb:
>
1
std::bind(&produce<std::string>, my_queue)

muss in deinem Fall natürlich
1
std::bind(&produce<int>, my_queue)

heissen

von Slippin J. (gustavo_f)


Lesenswert?

Sven S. schrieb:
> as hat damit zu tun, das dein bind ambigous ist. Um dies zu beheben,
> musst du das ganze casten, schau mal hier nach:

Ja ich denke, daran wird's liegen.

>
> Ich denke, bei dir müsste das so lauten:
> auto f = std::bind(static_cast<void *( Queue<int>& )>(&produce),
> my_queue);

Leider funktioniert das nicht:
error: invalid static_cast from type ‘<unresolved overloaded function 
type>’ to type ‘void*(Queue<int>&)’
   auto f = std::bind(static_cast<void *( Queue<int>& )>(&produce), 
my_queue);

Kenne mich mit static_cast auch kaum aus. Falls jemand da nicht noch 
eine tolle Idee, werde ich mich da mal reinknien müssen.

The D. schrieb:
> muss in deinem Fall natürlich
> std::bind(&produce<int>, my_queue)

Das war mein erster Versuch, hat leider nicht funktioniert.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

1
auto f = std::bind(static_cast<void (*) (Queue<int>&)> (produce), my_queue); // <<<  FEHLER
2
  
3
// Alternative
4
auto g = [&] () { produce (my_queue); };

http://ideone.com/Ktir8Q

von Slippin J. (gustavo_f)


Lesenswert?

Dr. Sommer schrieb:
> auto f = std::bind(static_cast<void (*) (Queue<int>&)> (produce),
> my_queue); // <<<  FEHLER
>
> // Alternative
> auto g = [&] () { produce (my_queue); };

Damit läuft's! Vielen Dank!

von Mikro 7. (mikro77)


Lesenswert?

Du überlädst produce() 
(https://en.wikipedia.org/wiki/Function_overloading#Rules_in_function_overloading).

Die beiden Funktionen haben die Signaturen 
(https://en.wikipedia.org/wiki/Type_signature):
1
void(*)(Queue<int>&)
2
void(*)(Queue<std::string>&)

Mit dem static_cast wählst du die gewünschte Signatur aus.
1
static_cast<void(*)(Queue<int>&)>(&produce)

Die Alternative 
(https://en.wikipedia.org/wiki/C%2B%2B11#Lambda_functions_and_expressions) 
hat den schönen Effekt, dass der Compiler anhand des Arguments selbst 
die richtige produce() Funktion auswählt. Das kann man hier machen, ist 
aber manchmal der falsche Ansatz.

Am einfachsten im konkreten Fall wäre es wohl deinen beiden produce() 
Funktionen unterschiedliche Namen zu geben, da sie offensichtlich 
unterschiedliche Sachen machen. Dann klappts auch ohne cast und ist 
einfacher zu lesen. Allerdings schreibst du dass es nur ein 
Minimalbeispiel ist... Muss man halt immer im konkreten Fall gucken.

von Slippin J. (gustavo_f)


Lesenswert?

Mikro 7. schrieb:
> Mit dem static_cast wählst du die gewünschte Signatur aus

Aber warum kann der Compiler nicht selber die richtige Signatur 
auswählen auswählen? Sind ja zwei unterschiedliche. Sonst muss man das 
ja auch nicht über einen Cast machen.

Mikro 7. schrieb:
> dass der Compiler anhand des Arguments selbst
> die richtige produce() Funktion auswählt

Ja, das Lambda ist wirklich deutlich lesbarer als der static_cast.

von Dr. Sommer (Gast)


Lesenswert?

Gustavo F. schrieb:
> Aber warum kann der Compiler nicht selber die richtige Signatur
> auswählen auswählen?

Weil immer von innen nach außen berechnet wird... d.h. es wird erst ein 
(anhand seiner Parameter) passendes produce gesucht, und danach wird 
ein dazu passendes bind gesucht. Funktionen oder Poibter darauf anhand 
des Rückgabe Typs aufzulösen geht in C++ nicht (gibt's überhaupt eine 
Sprache die das kann? )

von Dr. Sommer (Gast)


Lesenswert?

Achja, und produce <int> geht nicht weil produce kein template ist, 
sondern lediglich eine überladene Funktion.

von Mikro 7. (mikro77)


Lesenswert?

Dr. Sommer schrieb:
> Achja, und produce <int> geht nicht weil produce kein template ist,
> sondern lediglich eine überladene Funktion.

Das könnte einen auf die Idee bringen, produce() als template zu 
definieren. :-)
1
#include <functional> // std::bind
2
#include <iostream>
3
4
template <typename T> struct Queue
5
{
6
  void push(T const &x) { std::cout << x << std::endl; }
7
} ;
8
9
template <typename T> void produce(Queue<T>&) ;
10
11
template <> void produce(Queue<int>         &q) { q.push(1234);    }
12
template <> void produce(Queue<std::string> &q) { q.push("Hallo"); }
13
14
int main()
15
{
16
  Queue<int> int_queue;
17
  auto f = std::bind(&produce<int>, int_queue); 
18
  f() ;
19
20
  Queue<std::string> str_queue;
21
  auto g = std::bind(&produce<std::string>, str_queue); 
22
  g() ;
23
24
  return 0 ;
25
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.