Inicialização de membro ao usar o construtor delegado

96

Comecei a experimentar o padrão C ++ 11 e encontrei esta pergunta que descreve como chamar seu ctor de outro ctor na mesma classe para evitar um método init ou semelhante. Agora estou tentando a mesma coisa com um código parecido com este:

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

Mas isso está me dando o erro: In constructor ‘config::Tokenizer::Tokenizer(std::stringstream*)’: /path/Tokenizer.cpp:14:20: error: mem-initializer for ‘config::Tokenizer::lines’ follows constructor delegationtentei mover a parte Tokenizer () primeiro e último na lista, mas isso não ajudou.

Qual é a razão por trás disso e como devo consertar? Tentei mover o lines(lines)para o corpo com this->lines = lines;e funcionou bem. Mas eu realmente gostaria de poder usar a lista de inicializadores.

lfxgroove
fonte

Respostas:

118

Quando você delega a inicialização do membro a outro construtor, supõe-se que o outro construtor inicializa o objeto completamente , incluindo todos os membros (ou seja, incluindo o linesmembro em seu exemplo). Você não pode, portanto, inicializar qualquer um dos membros novamente.

A citação relevante do Padrão é (ênfase minha):

(§12.6.2 / 6) Uma lista inicializador de mem pode delegar a outro construtor da classe do construtor usando qualquer class-or-decltype que denota a própria classe do construtor. Se um mem-initializer-id designa a classe do construtor, ele deve obrigatoriamente ser o único mem-initializer ; o construtor é um construtor delegante e o construtor selecionado pelo é o construtor de destino. [...]

Você pode contornar isso definindo a versão do construtor que recebe os argumentos primeiro :

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

e definir o construtor padrão usando delegação:

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

Como regra geral, você deve especificar totalmente a versão do construtor que recebe o maior número de argumentos e, em seguida, delegar das outras versões (usando os valores padrão desejados como argumentos na delegação).

jogojapan
fonte
2
Parece contra-intuitivo no início, mas está realmente ajudando na verdade!
Korchkidu de