Elementinitialisierung bei Verwendung eines delegierten Konstruktors

96

Ich habe angefangen, den C ++ 11-Standard auszuprobieren, und ich habe diese Frage gefunden , die beschreibt, wie Sie Ihren ctor von einem anderen ctor in derselben Klasse aufrufen, um eine init-Methode oder ähnliches zu vermeiden. Jetzt versuche ich dasselbe mit Code, der so aussieht:

hpp:

class Tokenizer
{
public:
  Tokenizer();
  Tokenizer(std::stringstream *lines);
  virtual ~Tokenizer() {};
private:
  std::stringstream *lines;
};

cpp:

Tokenizer::Tokenizer()
  : expected('=')
{
}

Tokenizer::Tokenizer(std::stringstream *lines)
  : Tokenizer(),
    lines(lines)
{
}

Dies gibt mir jedoch den Fehler: In constructor ‘config::Tokenizer::Tokenizer(std::stringstream*)’: /path/Tokenizer.cpp:14:20: error: mem-initializer for ‘config::Tokenizer::lines’ follows constructor delegationIch habe versucht, den Tokenizer () - Teil zuerst und zuletzt in der Liste zu verschieben, aber das hat nicht geholfen.

Was ist der Grund dafür und wie soll ich das beheben? Ich habe versucht lines(lines), den Körper this->lines = lines;stattdessen mit zu bewegen , und es funktioniert gut. Aber ich würde wirklich gerne die Initialisierungsliste verwenden können.

lfxgroove
quelle

Antworten:

118

Wenn Sie die Elementinitialisierung an einen anderen Konstruktor delegieren, wird davon ausgegangen, dass der andere Konstruktor das Objekt vollständig initialisiert , einschließlich aller Mitglieder (dh einschließlich des linesElements in Ihrem Beispiel). Sie können daher keines der Mitglieder erneut initialisieren.

Das relevante Zitat aus dem Standard lautet (Schwerpunkt Mine):

(§12.6.2 / 6) Eine mem-initializer-Liste kann unter Verwendung eines beliebigen Klassen- oder Dekltyps, der die Klasse des Konstruktors selbst bezeichnet, an einen anderen Konstruktor der Konstruktorklasse delegieren. Wenn eine mem-initializer-id die Klasse des Konstruktors angibt, ist sie der einzige mem-initializer . Der Konstruktor ist ein delegierender Konstruktor, und der vom Konstruktor ausgewählte Konstruktor ist der Zielkonstruktor. [...]

Sie können dies umgehen, indem Sie die Version des Konstruktors definieren, der zuerst Argumente akzeptiert :

Tokenizer::Tokenizer(std::stringstream *lines)
  : lines(lines)
{
}

und definieren Sie dann den Standardkonstruktor mithilfe der Delegierung:

Tokenizer::Tokenizer()
  : Tokenizer(nullptr)
{
}

In der Regel sollten Sie die Version des Konstruktors, die die meisten Argumente akzeptiert, vollständig angeben und dann von den anderen Versionen delegieren (wobei Sie die gewünschten Standardwerte als Argumente in der Delegierung verwenden).

jogojapan
quelle
2
Es scheint zunächst nicht intuitiv zu sein, hilft aber wirklich!
Korchkidu