web-dev-qa-db-fra.com

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

J'ai commencé à essayer la norme C++ 11 et j'ai trouvé this 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 similaire. Maintenant j'essaye la même chose avec du 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 delegation J'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 la lines(lines) vers le corps avec this->lines = lines; À la place et cela fonctionne très bien. Mais j'aimerais vraiment pouvoir utiliser la liste des initialiseurs.

Merci d'avance!

85
lfxgroove

Lorsque vous déléguez l'initialisation du membre à un autre constructeur, il est supposé que l'autre constructeur initialise complètement l'objet , y compris tous les membres (c'est-à-dire y compris le lines membre dans votre exemple). Vous ne pouvez donc pas initialiser à nouveau l'un des membres.

La citation pertinente de la norme est (soulignement le mien):

(§12.6.2/6) Une liste mem-initializer-list peut déléguer à un autre constructeur de la classe du constructeur en utilisant n'importe quelle classe ou decltype qui dénote la classe du constructeur elle-même. Si un mem-initializer-id désigne la classe du constructeur, ce sera le seul mem-initializer; le constructeur est un constructeur délégué, et le constructeur sélectionné par le est le constructeur cible. [...]

Vous pouvez contourner cela 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 complètement 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).

104
jogojapan