Überladungsauflösungsfehler beim Streamen von Objekten über implizite Konvertierung in Zeichenfolgen

Lesezeit: 12 Minuten

Uberladungsauflosungsfehler beim Streamen von Objekten uber implizite Konvertierung in Zeichenfolgen
Leichtigkeitsrennen im Orbit

Haftungsausschluss: ich wissen dass eine implizite Konvertierung in eine Zeichenfolge vermieden werden sollte und dass der richtige Ansatz ein wäre op<< Überlastung für Person.


Betrachten Sie den folgenden Code:

#include <string>
#include <ostream>
#include <iostream>

struct NameType {
   operator std::string() { return "wobble"; }
};

struct Person {
   NameType name;
};

int main() {
   std::cout << std::string("bobble");
   std::cout << "wibble";

   Person p;
   std::cout << p.name;
}

Es ergibt folgendes auf GCC 4.3.4:

prog.cpp: In function ‘int main()’:
prog.cpp:18: error: no match for ‘operator<<’ in ‘std::cout << p.Person::name’
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:112: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:121: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:131: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:169: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:173: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:177: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:97: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:184: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:111: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:195: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:204: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:208: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:213: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:217: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:225: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:229: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:125: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]

Wie kommt das kostenlose op<<(ostream&, string const&) schafft es nicht in das Overload-Set? Ist dies auf eine Kombination der gewünschten Überladung als Vorlageninstanziierung und … ADL zurückzuführen?

  • mögliches Duplikat von Warum führt der Compiler keine Typkonvertierung durch?

    – Armen Tsirunyan

    13. August 2011 um 22:22 Uhr

  • @Armen: Auf den ersten Blick vielleicht, aber die Antworten zeigen, dass es sich tatsächlich um ein anderes Problem handelt. Vielleicht.

    – Leichtigkeitsrennen im Orbit

    16. August 2011 um 9:44 Uhr


  • Selbst wenn es funktionieren würde, wäre es schlechtes Design. Stellen Sie sich vor, Sie hätten auch eine implizite Konvertierung von hinzugefügt NameType zu int. (Das ist wahrscheinlich nicht sinnvoll, aber Sie können sich eine Klasse mit mehreren sinnvollen impliziten Konvertierungsoperatoren vorstellen.) Jetzt cout << p.name wird zweideutig.

    – Japreis

    20. November 2014 um 15:10 Uhr


  • @japreiss: Freund, lies das erste Linie der Frage.

    – Leichtigkeitsrennen im Orbit

    20. November 2014 um 15:16 Uhr

  • Ich meinte, dass es schlechtes Design ist, die impliziten Konvertierungen in Stream IO zu verwenden, sie gar nicht erst zu haben.

    – Japreis

    20. November 2014 um 15:27 Uhr

14.8.1/4 in C++98

Implizite Konvertierungen (Klausel 4) werden an einem Funktionsargument durchgeführt, um es in den Typ des entsprechenden Funktionsparameters zu konvertieren, wenn der Parametertyp no enthält Template-Parameter die am Template-Argumentabzug teilnehmen.

Hier möchten Sie eine Instanziierung von

template <class charT, class traits, class Allocator>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>&,
               const basic_string<charT, traits, Allocator>&);

abzuleiten, ohne explizit Template-Argumente anzugeben. Alle Argumente enthalten also a Template-Parameter das am Template-Argumentabzug teilnimmt und daher keiner von ihnen seinen Wert aus einer impliziten Konvertierung ziehen kann.

  • Diese Sprache ist so schwer zu verstehen. Also “der Parametertyp” hier ist std::basic_string<charT, traits, Allocator> (für einige Spezialisierungen), und diese Art von Kurs enthält template-parameters die am Abzug teilnehmen, weil ich mich auf den Abzug verlasse. Blöd!

    – Leichtigkeitsrennen im Orbit

    26. September 2011 um 13:58 Uhr

Uberladungsauflosungsfehler beim Streamen von Objekten uber implizite Konvertierung in Zeichenfolgen
Bo Persson

Es ist, weil es eine Vorlage ist.

Damit dies funktioniert, müssten Sie zuerst die Vorlage instanziieren und anschließend den Konvertierungsoperator verwenden. Das ist die falsche Reihenfolge, also funktioniert es nicht.


Es spielt keine Rolle, ob Sie zuvor im Programm einen bestimmten Operator verwendet haben oder nicht. Jede Nutzung wird gesondert betrachtet

Die als Kandidaten betrachteten Überladungen sind diejenigen, bei denen alle Vorlagenparameter von std::ostream abgeleitet werden können, oder diejenigen, die Mitglieder dieser Klasse sind.


Was passiert, wenn wir einen Nicht-Template-Operator hinzufügen?

#include <string> 
#include <ostream> 
#include <iostream>  

struct NameType {
   operator std::string() { return "wobble"; } 
};  

struct Person {
    NameType name;
};  

void operator<<(std::ostream& os, const std::string& s)   // ** added **
{ std::operator<<(os, s); }

int main() 
{    
    std::cout << std::string("bobble");
    std::cout << "wibble";

     Person p;
     std::cout << p.name; 
}  

Jetzt funktioniert es und gibt aus

 bobblewibblewobble

  • Es wird durch die erste Zeile instanziiert int main(). Nein?

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:01 Uhr

  • Danke für deine Bearbeitung. Ich bin immer noch nicht überzeugt; Die vorherige Template-Instanziierung hätte einen brauchbaren Kandidaten bringen sollen (der dauert std::string) ins Leben gerufen. Ihr Arbeitsbeispiel weist eine Änderung des Namensraums sowie Nicht-Vorlagenhaftigkeit auf, was zu Nawaz’ überzeugender ADL-Theorie passen würde.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 12:58 Uhr


  • Dies sind die gleichen Namespaces, die zuvor durchsucht wurden, der von cout und das von NameType. Was ich hier gezeigt habe, ist der benutzerdefinierte Konvertierungsoperator wird genutzt wenn der Kandidat keine Vorlage ist, der Ausgabeoperator jedoch nicht einmal ein Kandidat wenn es sich um eine Vorlage handelt.

    – Bo Persson

    13. Juli 2011 um 13:49 Uhr

  • Es wäre wirklich schrecklich, wenn die Tatsache, dass die Vorlage durch eine nicht damit zusammenhängende frühere Verwendung instanziiert wurde, den Satz von Kandidaten für die Überladungsauflösung ändern würde. Das plötzliche Löschen von totem Code, der einige benötigte Vorlagen instanziiert, kann viele verwirrende Fehlermeldungen verursachen.

    – Namen53

    20. September 2011 um 16:48 Uhr

  • Nur für die Genauigkeit, operator<< sollte std::ostream& zurückgeben (eigentlich std::basic_ostream)

    – Andriy Tylychko

    22. September 2011 um 13:46 Uhr


1646321418 420 Uberladungsauflosungsfehler beim Streamen von Objekten uber implizite Konvertierung in Zeichenfolgen
Nawaz

Dies liegt daran, dass benutzerdefinierte Konvertierungsfunktionen in ADL nicht berücksichtigt werden. ADL bedeutet Überladungssatz enthält Überladungsfunktion(en) aus dem Namensraum, in dem die Streit ist definiert. Hier die Art des Arguments zu operator<< ist NameType aber operator << (std::ostream&, const NameType&) wurde nicht in dem Namensraum definiert, in dem NameType ist definiert. Daher der Fehler, nach einer geeigneten Überladung zu suchen stoppt genau da. Das ist ADL. ADL geht nicht weiter, um die Definition von zu untersuchen NameType um festzustellen, ob es eine benutzerdefinierte Konvertierungsfunktion definiert oder nicht.

Sie erhalten der gleiche Fehler wenn du folgendes machst:

NameType name;
std::cout << name ; //error: user-defined conversion not considered.

Du musst Gießen es:

std::cout << (std::string)name << std::endl; //ok - use std::string()

Außerdem haben Sie möglicherweise mehr als eine benutzerdefinierte Konvertierungsfunktion:

std::cout << (int)name << std::endl; //ok - use int() instead

Ausgabe bei Idee:

wobble
100

  • Können Sie erklären, wie ADL hier ins Spiel kommt und warum die benutzerdefinierte Konvertierung nicht berücksichtigt wird?

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:01 Uhr

  • Ich weiß, dass es ohne die implizite Konvertierung funktioniert. Ich versuche herauszufinden, warum es nicht mit.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:04 Uhr

  • @Tomalak: Erklärung hinzugefügt. 🙂

    – Nawaz

    13. Juli 2011 um 10:20 Uhr

  • Mein Verstand ist ein bisschen wund, aber ich glaube, ich kann folgen. Fügen Sie einige Standardreferenzen für einen goldenen Stern hinzu. 🙂 Zu meiner eigenen Beruhigung darf ich bestätigen, dass ich richtig verstanden habe: die op<< Überlast Ich möchte nicht in gefunden NameType‘s Namensraum, aber std::string‘s (dh std). Und die Überladungsauflösung für eine benutzerdefinierte Konvertierung geht nicht weit genug, um sie zu finden. Ja?

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:37 Uhr


  • Warte mal, warum nicht std betrachtet? Die LHS (std::cout) sollte das per ADL ins Overload-Set bringen?

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:46 Uhr

Die Konvertierung in eine Zeichenfolge wird nur in einigen Fällen aufgerufen:

a) ausdrücklich angefordert (string) p.name

b) Zuordnung zu einem String string a = p.name

C) …

Wenn der vorliegende Fall nicht passt, können Sie die Anrufung erzwingen ostream<<(ostream&,string) auf mindestens zwei Arten:

  1. http://ideone.com/SJe5W Namenstyp machen sein eine Zeichenfolge (durch öffentliche Vererbung).

  2. zum Fall gehen ein): Fordern Sie explizit die Konvertierung an, wie im Beispiel mit der Konvertierung nach gezeigt (int).

Ich bevorzuge wirklich die Option 1.

Uberladungsauflosungsfehler beim Streamen von Objekten uber implizite Konvertierung in Zeichenfolgen
iammilind

Das liegt daran, dass benutzerdefinierte Konvertierungen nicht verkettet werden können. Um es an einem Beispiel zu erklären:

struct A {
  void operator = (const int i);
};

struct B {
  operator int ();
}

A a;
B b;
a = b;  // error! because, compiler will not match "A::operator=" and "B::operator int"

Hier ist die ähnliche Frage, die ich irgendwann einmal gestellt habe.

In Ihrem Fall sind Ihre ersten benutzerdefinierten Conversions:

(1) NameType::operator std::string()

(2) operator <<(ostream&, const std::string&) das ist etwas wie ostream::operator<<(std::string&).

Wenn du schreibst, cout << p.name; Jetzt zwei Arten von Objekte von Angesicht zu Angesicht kommen:

ostream (LHS) <====> NameType (RHS)

Jetzt, operator <<(ostream&, const string&) wird aufgerufen nur wenn RHS ist string. Aber hier ist es NameType; es wird also nicht aufgerufen.

Und, NameType::operator string () wird aufgerufen nur wenn, LHS ist string. Aber hier ist es ostream; es wird also nicht aufgerufen.

Um diese Gleichung wahr zu machen; Bot die oben genannten Operatormethoden sollten vom Compiler aufgerufen werden. Aber das wird von C++ nicht unterstützt. Warum es nicht unterstützt wird, ist in dem Link beschrieben, den ich oben gepostet habe.

  • Es gibt also eine benutzerdefinierte Konvertierung in der op stringaber wo ist der andere?

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:03 Uhr

  • Ich verstehe nicht, wie eine Funktion eine Konvertierung ist.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:13 Uhr

  • @Tomalak, operator<<() ist eine Funktion und darin, die Sie zu empfangen versuchen const std::string&; das wird niemals übereinstimmen, es sei denn, es ist explizit (const) std::string& ist bestanden. Du gehst vorbei NameType stattdessen [NameType::string operator() is not considered, if the LHS has non-std::string (here ostream) type]. Versuche zu bestehen NameType mit Typumwandlung zu std::stringdann wird es funktionieren, denn jetzt übergibst du explizit std::string.

    – iammilind

    13. Juli 2011 um 10:17 Uhr


  • Ich verstehe nicht, wie das zwei Konvertierungen sind. Wenn das erste (zu std::string) wurde bereits durchgeführt, dann ja wir sind Passieren a std::string in.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:24 Uhr

  • Abgesehen davon, dass es überhaupt nicht gleichwertig ist.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 12:56 Uhr

  • Es gibt also eine benutzerdefinierte Konvertierung in der op stringaber wo ist der andere?

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:03 Uhr

  • Ich verstehe nicht, wie eine Funktion eine Konvertierung ist.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:13 Uhr

  • @Tomalak, operator<<() ist eine Funktion und darin, die Sie zu empfangen versuchen const std::string&; das wird niemals übereinstimmen, es sei denn, es ist explizit (const) std::string& ist bestanden. Du gehst vorbei NameType stattdessen [NameType::string operator() is not considered, if the LHS has non-std::string (here ostream) type]. Versuche zu bestehen NameType mit Typumwandlung zu std::stringdann wird es funktionieren, denn jetzt übergibst du explizit std::string.

    – iammilind

    13. Juli 2011 um 10:17 Uhr


  • Ich verstehe nicht, wie das zwei Konvertierungen sind. Wenn das erste (zu std::string) wurde bereits durchgeführt, dann ja wir sind Passieren a std::string in.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 10:24 Uhr

  • Abgesehen davon, dass es überhaupt nicht gleichwertig ist.

    – Leichtigkeitsrennen im Orbit

    13. Juli 2011 um 12:56 Uhr

924800cookie-checkÜberladungsauflösungsfehler beim Streamen von Objekten über implizite Konvertierung in Zeichenfolgen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy