Forum: PC-Programmierung C++ Konstruktorüberladung bzw. Typkonvertierung


von Micha (Gast)


Lesenswert?

Hallo,

ich habe bisher wenig bis keine Erfahrung mit C++ und darf jetzt Code 
eines Kollegen erweitern, der nicht mehr greifbar ist. Es gibt eine 
Klasse 'Message' mit den beiden Konstruktoren:

message.cpp
1
Message::Message(std::initializer_list<std::uint8_t> data) :
2
  payloadData(data.begin(), data.end()) {}
3
4
Message::Message(std::vector<std::uint8_t> data) :
5
  payloadData(std::move(data)) {}
6
7
//Message::Message(std::string data) :
8
//  payloadData(data.begin(), data.end()) {}

message.h
1
class Message
2
{
3
public:
4
  explicit Message(std::initializer_list<std::uint8_t> data = {});
5
  explicit Message(std::vector<std::uint8_t> data);
6
//  explicit Message(std::string data);
7
8
protected:
9
  std::vector<std::uint8_t> payloadData;
10
}

Ich möchte jetzt ein Objekt dieser Klasse mit einem std::string 
instanzieren.

somefile.cpp
1
  void sendMessage(const Message& sendMessage)
2
  {
3
    // some code
4
  }
5
6
  void run()
7
  {
8
    std::vector<std::string> identifier;
9
    // some code, fill identifier
10
11
    // send messages
12
    for (const auto& dataSet : identifier)
13
    {
14
      sendMessage(Message{ dataSet }); // dataSet.c_str() also doesn't work
15
    }
16
  }

In Visual Studio 2017 bekomme ich die Meldungen:
1
Fehler (aktiv)  E0289  Keine Instanz des Konstruktors ""Message::Message"" stimmt mit der Argumentliste überein. Argumenttypen sind: (const char *)
2
Fehler  C2440  "Initialisierung": "initializer list" kann nicht in "Message" konvertiert werden

Wenn ich den oben auskommentierten dritten Konstruktor hinzufüge, kann 
sich der Kompiler an anderer Stelle nicht mehr für einen Konstruktor 
entscheiden, da dann zwei in Frage kommen.
1
{
2
  std::vector<std::uint8_t> buffer(nubmerOfBytes);
3
  // some code
4
  return Message{ { buffer.begin(), buffer.end() } };
5
}
1
Fehler (aktiv)  E0309  Mehrere Instanzen des Konstruktors ""Message::Message"" stimmen mit der Argumentliste überein:
2
            Funktion "Message::Message(std::vector<boost::asio::detail::buffered_stream_storage::byte_type, std::allocator<boost::asio::detail::buffered_stream_storage::byte_type>> data)"
3
            Funktion "Message::Message(std::string data)"
4
            Argumenttypen sind: ({...})
5
Fehler  C2440  "Initialisierung": "initializer list" kann nicht in "Message" konvertiert werden

Wie kann ich das Problem lösen?

von Oliver S. (oliverso)


Lesenswert?

Micha schrieb:
> Ich möchte jetzt ein Objekt dieser Klasse mit einem std::string
> instanzieren.

Da die Konstruktoren von Message nur entweder eine Initializer-List mit 
uint8_t-Werten oder einen Vector mit uint8_t-Werten haben wollen, geht 
das nicht.

Jetzt könnte man vermuten, daß diese uint8_t-Werte ASCII-Zeichen sein 
könnten. Das musst du aber selber aus den Sourcen heraus knobeln.

Wenn das so ist, nimm die Zeichen des Strings, schreib die in einen 
Vector<uint8_t>, dann klappt es auch mit dem Konstruktor.

Der auskommentierte dritte Konstruktor allerdings sollte einfach so 
funktionieren.

Micha schrieb:
> return Message{ { buffer.begin(), buffer.end() } };

Wer hat denn diese Zeile geschrieben?

Oliver

: Bearbeitet durch User
von Micha (Gast)


Lesenswert?

Oliver S. schrieb:
> Der auskommentierte dritte Konstruktor allerdings sollte einfach so
> funktionieren.

Tut er auch - an dieser Stelle. Dafür entsteht ein Fehler an besagter 
anderer Stelle.

Oliver S. schrieb:
> Micha schrieb:
>> return Message{ { buffer.begin(), buffer.end() } };
>
> Wer hat denn diese Zeile geschrieben?

Der damalige Autor an einer ganz anderen Stelle im Code.

von Oliver S. (oliverso)


Lesenswert?

Micha schrieb:
> Der damalige Autor an einer ganz anderen Stelle im Code.

Tja, dann hat entweder das noch nie compiliert, oder du hast nicht allen 
Code von Message gezeigt. Im letzteren Fall wirst das Problem selber 
lösen müssen.

Oliver

von Micha (Gast)


Lesenswert?

Oliver S. schrieb:
> Tja, dann hat entweder das noch nie compiliert, oder du hast nicht allen
> Code von Message gezeigt. Im letzteren Fall wirst das Problem selber
> lösen müssen.

Es kompiliert solange ich "meinen" Konstruktor auskommentiert lasse. 
Sobald ich den aber dazu kompilieren möchte, kann sich der Kompiler 
nicht mehr zwischen dem ersten (?) und dem dritten Konstruktor 
entscheiden.

In Message ist noch mehr drin, aber das imo relevante hab ich gepostet. 
Dank deines Hinweises habe ich es jetzt auch kompiliert, nachdem ich
1
    // send messages
2
    for (const auto& dataSet : identifier)
3
    {
4
      sendMessage(Message{ dataSet }); // dataSet.c_str() also doesn't work
5
    }
geändert habe in
1
    // send messages
2
    for (const auto& dataSet : identifier)
3
    {
4
      sendMessage(Message{ {dataSet.begin(), dataSet.end()} });
5
    }

Vielen Dank für den Hinweis die Daten vorher zu konvertieren. Ist 
vielleicht nicht ganz so elegant, aber Hauptsache es funktioniert.

von Oliver S. (oliverso)


Lesenswert?

Nachtrag:  Die Zeile
> return Message{ { buffer.begin(), buffer.end() } };

könntest du durch eine explizite Typangabe entschärfen:
1
return Message{ std::vector<uint8_t>{ buffer.begin(), buffer.end() } };

Das würde ich an der Stelle als guten Programmierstil ansehen.

Oliver

von Micha (Gast)


Lesenswert?

Oliver S. schrieb:
> Das würde ich an der Stelle als guten Programmierstil ansehen.

Super! Nochmals vielen Dank! Damit läuft auch die Variante mit dem 3. 
Konstruktor.

von Oliver S. (oliverso)


Lesenswert?

Oliver S. schrieb:
> return Message{ std::vector<uint8_t>{ buffer.begin(), buffer.end() } };

Man kann vermutlich zwar auch einfach schreiben:
1
return Message{ buffer };

aber manchmal ist einfach einfach zu kompliziert ;)

Oliver

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Micha schrieb:
> In Visual Studio 2017 bekomme ich die Meldungen:
>
1
> Fehler (aktiv)  E0289  Keine Instanz des Konstruktors 
2
> ""Message::Message"" stimmt mit der Argumentliste überein. Argumenttypen 
3
> sind: (const char *)
4
> Fehler  C2440  "Initialisierung": "initializer list" kann nicht in 
5
> "Message" konvertiert werden
6
>

Fehlermeldung passt nicht zum gezeigten Code. Wenn Du auch in der Lage 
sein möchtest, eine Message von einem `const char*` zu konstruieren 
(z.B. string literals), dann solltest Du einen entsprechenden Konstrukt 
hinzufügen. Ansonsten gibt es keinen Grund, warum der gezeigte Code 
nicht funktionieren sollte.

>
1
> {
2
>   std::vector<std::uint8_t> buffer(nubmerOfBytes);
3
>   // some code
4
>   return Message{ { buffer.begin(), buffer.end() } };
5
> }
6
>

`buffer` ist vom Typen `std::vector<std::uint8_t>` und die korrekte 
Syntax, um damit einen Message zu konstruieren, wäre:
1
    return Message( buffer );

`{ buffer.begin(), buffer.end() }` wäre vom Typen 
`std::initializer_list<std::vector<std::uint8_t>::iterator>` und dafür 
gibt es keinen Konstruktor.

> Wie kann ich das Problem lösen?

Immer aufmerksam die Fehlermeldung lesen ;-)

von Oliver S. (oliverso)


Lesenswert?

Torsten R. schrieb:
> `{ buffer.begin(), buffer.end() }` wäre vom Typen
> `std::initializer_list<std::vector<std::uint8_t>::iterator>` und dafür
> gibt es keinen Konstruktor.

Da wird erst der Konstruktor für vector(itarator, iterator) aufgerufen, 
und damit dann der von Message(vector). Da vector ein Template ist, 
funktioniert das so. Erst, wenn da String ins Spiel kommt, für den es 
ebenfalls einen Konstruktor String(iterator, iterator) gibt, wird das 
uneindeutig.

Die Fehlermeldungen sind schon alle richtig.

Oliver

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Oliver S. schrieb:
> Da wird erst der Konstruktor für vector(itarator, iterator) aufgerufen,
> und damit dann der von Message(vector). Da vector ein Template ist,
> funktioniert das so.

Das hat wenig damit zu tun, dass `std::vector<>` ein Template ist 
(`std::basic_string<>` ist auch ein Template), sondern damit, dass eine 
Konvertierung nötig ist (weil es eben keinen c'tor für 
`std::initializer_list<std::vector<std::uint8_t>::iterator>` gibt) und 
die Konvertierung dann nicht eindeutig ist, weil `std::uint8_t` (bzw. 
boost::asio::detail::buffered_stream_storage::byte_type) wahrscheinlich 
ein Alias für `char` ist.

von Oliver S. (oliverso)


Lesenswert?

Torsten R. schrieb:
> weil std::uint8_t` (bzw.
> boost::asio::detail::buffered_stream_storage::byte_type) wahrscheinlich
> ein Alias für `char` ist.

Das 'u' in uint8_t lässt vermuten, daß das mit ziemlicher Sicherheit ein 
unsigned char ist ;) (Und boost ist da auch nicht dabei)
Und ein std::string ist kein std::vector<char>. Da gibt es keinerlei 
möglichen Mehrdeutigkeiten.

Das Problem, welches mit dem Konstruktor Message(string) zur 
Fehlermeldung führt, kommt daher, daß es dann zwei gleichwertige 
Möglichkeiten zur Auflösung des impliziten cast von {iterator, iterator} 
zu einem von Message akzeptierten Typ gibt.

Oliver

: Bearbeitet durch User
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.