Initialisation des membres lors de l'utilisation d'un constructeur délégué

96

J'ai commencé à essayer le standard C ++ 11 et j'ai trouvé cette question qui décrit comment appeler votre ctor à partir d'un autre ctor de la même classe pour éviter d'avoir une méthode init ou autre. Maintenant, j'essaye la même chose avec un code qui ressemble à ceci:

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)
{
}

Mais cela me donne l'erreur: In constructor ‘config::Tokenizer::Tokenizer(std::stringstream*)’: /path/Tokenizer.cpp:14:20: error: mem-initializer for ‘config::Tokenizer::lines’ follows constructor delegationj'ai essayé de déplacer la partie Tokenizer () en premier et en dernier dans la liste, mais cela n'a pas aidé.

Quelle est la raison derrière cela et comment dois-je y remédier? J'ai essayé de déplacer le lines(lines)vers le corps avec à la this->lines = lines;place et cela fonctionne très bien. Mais j'aimerais vraiment pouvoir utiliser la liste des initialiseurs.

lfxgroove
la source

Réponses:

118

Lorsque vous déléguez l'initialisation du membre à un autre constructeur, on suppose que l'autre constructeur initialise complètement l'objet , y compris tous les membres (c'est-à-dire en incluant le linesmembre dans votre exemple). Vous ne pouvez donc réinitialiser aucun des membres.

La citation pertinente de la norme est (c'est moi qui souligne):

(§12.6.2 / 6) Une liste d'initialiseur de mémoire peut déléguer à un autre constructeur de la classe du constructeur en utilisant n'importe quel type de classe ou de type décrivant la classe du constructeur elle-même. Si un mem-initializer-id désigne la classe du constructeur, il doit être le seul mem-initializer ; le constructeur est un constructeur délégant et le constructeur sélectionné par le est le constructeur cible. [...]

Vous pouvez contourner ce problème en définissant la version du constructeur qui prend les arguments en premier :

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

puis définissez le constructeur par défaut à l'aide de la délégation:

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

En règle générale, vous devez spécifier entièrement la version du constructeur qui prend le plus grand nombre d'arguments, puis déléguer à partir des autres versions (en utilisant les valeurs par défaut souhaitées comme arguments dans la délégation).

jogojapan
la source
2
Cela semble contre-intuitif au début, mais cela aide vraiment!
Korchkidu