Inicjalizacja elementu członkowskiego podczas korzystania z konstruktora delegowanego

97

Zacząłem wypróbowywać standard C ++ 11 i znalazłem to pytanie, które opisuje, jak wywołać twojego ctora z innego ctora w tej samej klasie, aby uniknąć posiadania metody init lub podobnej. Teraz próbuję tego samego z kodem, który wygląda następująco:

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

Ale to daje mi błąd: In constructor ‘config::Tokenizer::Tokenizer(std::stringstream*)’: /path/Tokenizer.cpp:14:20: error: mem-initializer for ‘config::Tokenizer::lines’ follows constructor delegationpróbowałem przenieść część Tokenizera () jako pierwszą i ostatnią na liście, ale to nie pomogło.

Jaki jest tego powód i jak mam to naprawić? Zamiast tego próbowałem przenieść lines(lines)do ciała this->lines = lines;i działa dobrze. Ale naprawdę chciałbym móc korzystać z listy inicjalizacyjnej.

lfxgroove
źródło

Odpowiedzi:

119

Kiedy delegujesz inicjalizację elementu członkowskiego do innego konstruktora, istnieje założenie, że inny konstruktor całkowicie inicjuje obiekt , w tym wszystkich członków (tj. W tym element linesczłonkowski w Twoim przykładzie). Dlatego nie możesz ponownie zainicjować żadnego z członków.

Odpowiedni cytat ze standardu to (wyróżnienie moje):

(§12.6.2 / 6) Mem-initializer-list może delegować do innego konstruktora klasy konstruktora przy użyciu dowolnego typu class-or-decltype, który oznacza klasę konstruktora. Jeśli mem-initializer-id wyznacza klasę konstruktora, będzie to jedyny mem-initializer ; konstruktor jest konstruktorem delegującym, a konstruktor wybrany przez funkcję jest konstruktorem docelowym. […]

Możesz obejść ten problem, definiując wersję konstruktora, która najpierw pobiera argumenty :

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

a następnie zdefiniuj domyślny konstruktor przy użyciu delegacji:

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

Zasadniczo należy w pełni określić tę wersję konstruktora, która przyjmuje największą liczbę argumentów, a następnie delegować ją z innych wersji (używając żądanych wartości domyślnych jako argumentów w delegacji).

jogojapan
źródło
2
Na początku wydaje się to sprzeczne z intuicją, ale naprawdę pomaga!
Korchkidu