Inicialización de miembros mientras se usa el constructor delegado

96

Comencé a probar el estándar C ++ 11 y encontré esta pregunta que describe cómo llamar a su ctor desde otro ctor en la misma clase para evitar tener un método init o similar. Ahora estoy intentando lo mismo con un código que se ve así:

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

Pero esto me está dando el error: In constructor ‘config::Tokenizer::Tokenizer(std::stringstream*)’: /path/Tokenizer.cpp:14:20: error: mem-initializer for ‘config::Tokenizer::lines’ follows constructor delegationintenté mover la parte Tokenizer () primero y último en la lista, pero eso no ayudó.

¿Cuál es la razón detrás de esto y cómo debo solucionarlo? En su lugar, intenté mover el lines(lines)al cuerpo con this->lines = lines;y funciona bien. Pero realmente me gustaría poder usar la lista de inicializadores.

lfxgroove
fuente

Respuestas:

118

Cuando delega la inicialización del miembro a otro constructor, se supone que el otro constructor inicializa el objeto por completo , incluidos todos los miembros (es decir, incluido el linesmiembro en su ejemplo). Por lo tanto, no puede inicializar ninguno de los miembros nuevamente.

La cita relevante de la Norma es (el énfasis es mío):

(§12.6.2 / 6) Una mem-initializer-list puede delegar en otro constructor de la clase del constructor usando cualquier clase-o-decltype que denote la propia clase del constructor. Si un mem-initializer-id designa la clase del constructor, será el único mem-initializer ; el constructor es un constructor delegante, y el constructor seleccionado por es el constructor de destino. [...]

Puede solucionar esto definiendo la versión del constructor que toma argumentos primero :

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

y luego defina el constructor predeterminado usando delegación:

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

Como regla general, debe especificar completamente la versión del constructor que toma la mayor cantidad de argumentos y luego delegar de las otras versiones (usando los valores predeterminados deseados como argumentos en la delegación).

jogojapan
fuente
2
Parece contrario a la intuición al principio, ¡pero realmente está ayudando!
Korchkidu