C++Guns – RoboBlog

10.06.2015

explicit constructor (Bug of the day 9)

Filed under: Allgemein — Tags: — Thomas @ 18:06

Hmm nicht einfach diesen Fehler zu erklären. Man braucht eine Basis Klasse, zwei abgeleitete Klassen und eine template Funktionen bei der man den Template Teil vergessen hat.

Die Idee ist folgende:
Irgendeine Funktion die irgendeinen Algorithmus ausführt, bekommt ihre Daten über einen Container per Funktionsparameter. Der Container kann alles mögliche sein: vector, list, stack array oder eigene Klassen die einen standard Container erweitern.
Für den Funktionsparameter, also den Container, benutzt man templates, damit der Algorithmus auf allen möglichen Datenstrukturen funktioniert und nur einmal implementiert werden muss.
Das ist gängige Praxis und funktioniert gut.

Nun habe ich zwei neue Klassen erstellt, die von z.B. std::vector erben. Im weiteren ist das die Basis Klasse. Diese Klassen speichern noch eine weitere Information. Welche, ist egal für dieses Beispiel. Nennen wir diese neuen Container Klassen Derived und Derived2.

Die Funktion sieht also so aus:

void func(const Derived &container);

Ich benutzt keine Templates, weil ich sie wie gesagt vergessen hatte. Das war Fehler Nr. 1. Der Code compilierte dennoch ;)

Da die neuen Container Klassen Derived und Derived2 beide von der Klasse Base erben, dachte ich, ein Constructor der eine Instanz der Base Klasse entgegen nimmt ist nicht soo verkehrt. Es ist dann bequem möglich seine Container mit den Standard Container von c++ zu initialisieren. Das war der zweite Fehler. Es ist im Grunde nicht falsch, nur man braucht es nicht, da wir ja templates nutzen. (Aber nicht implementieren).

Beim dritten Fehler habe ich die Coding Style Regeln nicht beachtet. Die besagt, dass man einen Constructor mit nur einem Argument als explicit deklarieren sollte. Siehe z.B. den Kram von google [1]

Normally, if a constructor can be called with one argument, it can be used as a conversion. Declaring a constructor explicit prevents it from being invoked implicitly as a conversion

Und genau das ist passiert. Ich habe func() mit einem Derived2 Object aufgerufen, und es wurde mangels templates in ein Derived Object umgewandelt. Dabei gingen natürlich die zusätzlichen Informationen verloren, welche diese Klassen speichern sollten.
Eine direkte Umwandlung der beiden Derived Klassen hatte ich nicht implementiert, da sie nicht sinnvoll sind.
Die Umwandlung erfolge über die Basis Klasse. Ein Derived2 Object ist ein Base Object. Und Derived kann mit Base Objecten initialisiert werden.

Das ist alles valid und der Compiler sieht kein Fehler. Aber es sollte zumindest ein Warning für den nicht expliciten Constructor geben. Zumindest bei GNU und clang habe ich nichts dergleichen gefunden. Der Intel Compiler kann es aber wohl [2] (Diagnostic 2304: non-explicit constructor with single argument may cause implicit type conversion).

Was lernen wir daraus? Coding Styles, und sich auch daran halten, retten Leben ;) Und nicht so bescheuert Programmieren ;)

Hätte man den Fehler durch ausgiebiges Testen der Funktion hätte finden können? Ja ich denke schon. Aber der Test muss gut sein. In meinem Fall hatte ich nur ein paar einfache Daten getestet und nichts bemerkt. Erst als ich einen anderen Fehler mit dem Debugger suchte ist es mir aufgefallen.

Im Anhang noch der Beispiel Code.explicitctor.cpp.zip

[1] https://google-styleguide.googlecode.com/svn/trunk/cppguide.html
[2] https://software.intel.com/en-us/forums/topic/335573

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress