Construtores de Conversão no C++

construtores

Por padrão, o C++ tratará qualquer construtor como um operador de conversão implícito. Considere o seguinte caso:

class Fraction {
private:
	int m_numerator;
	int m_denominator;
 
public:
	Fraction(int numerator=0, int denominator=1) :
		m_numerator(numerator), m_denominator(denominator) {
		assert(denominator != 0);
	}

	Fraction(const Fraction &copy) :
		m_numerator(copy.m_numerator), m_denominator(copy.m_denominator) {
		std::cout << "Construtor de cópia chamado\n";
	}

	friend std::ostream& operator<<(std::ostream& out, const Fraction &f1);
	int getNumerator() { return m_numerator; }
	void setNumerator(int numerator) { m_numerator = numerator; }
};
 
std::ostream& operator<<(std::ostream& out, const Fraction &f1) {
	out << f1.m_numerator << "/" << f1.m_denominator;
	return out;
}
 
Fraction makeNegative(Fraction f) {
    f.setNumerator(-f.getNumerator());
    return f;
}
 
int main() {
    std::cout << makeNegative(6); // observe que é passado um inteiro
 
    return 0;
}

Embora a função makeNegative() esteja esperando um objeto da classe Fraction, no exemplo, em seu lugar, é fornecido o inteiro literal “6”. Como a classe Fraction possui um construtor que aceita um único inteiro, o compilador então converte implicitamente o literal “6” em um objeto Fraction. Ele faz isso inicializando o parâmetro makeNegative() usando o construtor Fraction(int, int).

Como “f” é um objeto da classe Fraction, o valor de retorno de makeNegative() é copiado de volta para o main, que então passa para o operador sobrecarregado “<<". Consequentemente, o programa acima imprime: [code] Construtor de cópia chamado -6/1 [/code] A conversão implícita funciona para todos os tipos de inicialização (direta, uniforme e cópia). Os construtores elegíveis para serem usados ​​para conversões implícitas são chamados de Construtores de Conversão. Antes do C++ 11, somente os construtores que usavam um parâmetro poderiam converter construtores. No entanto, com a nova sintaxe de inicialização uniforme em C++ 11, essa restrição foi suspensa e os construtores que usam vários parâmetros agora podem também converter construtores.

A palavra-chave explicit

Embora fazer conversões implícitas faça sentido no exemplo da classe Fraction, em outros casos, isso pode ser indesejável ou levar a comportamentos inesperados:

class MyString {
private:
	std::string m_string;
public:
	MyString(int x) {
		m_string.resize(x);
	}

	MyString(const char *string) {
		m_string = string;
	}

	friend std::ostream& operator<<(std::ostream& out, const MyString &s);
};
 
std::ostream& operator<<(std::ostream& out, const MyString &s) {
	out << s.m_string;
	return out;
}
 
int main() {
	MyString mine = 'x'; // em uso a inicialização de cópia de MyString
	std::cout << mine;
	return 0;
}

No exemplo acima, está sendo tentando inicializar uma string com um caractere. Como chars fazem parte da família integer, o compilador fará uso do construtor de conversão MyString(int) para implicitamente converter o char em um MyString. O programa irá imprimir MyString, para resultados inesperados.

Uma maneira de resolver esse problema é tornar os construtores (e as funções de conversão) explícitos por meio da palavra-chave explicit, que é colocada na frente do nome do construtor. Construtores e funções de conversão tornadas explícitas não serão usados para conversões implícitas ou inicialização de cópia:

class MyString {
private:
	std::string m_string;
public:
	explicit MyString(int x) {
		m_string.resize(x);
	}

	MyString(const char *string) {
		m_string = string;
	}

	friend std::ostream& operator<<(std::ostream& out, const MyString &s);
};
 
std::ostream& operator<<(std::ostream& out, const MyString &s) {
	out << s.m_string;
	return out;
}
 
int main() {
	MyString mine = 'x'; // erro de compilação, uma vez que MyString(int) é agora explicit e nenhum construtor irá servir
	std::cout << mine;
	return 0;
}

O programa acima não compilará, já que MyString(int) foi explicitado, e não foi possível encontrar um construtor de conversão apropriado para converter implicitamente ‘x’ para um objeto MyString.

No entanto, deve-se observar que tornar um construtor explícito apenas impede conversões implícitas. Conversões explícitas (via transmissão) ainda são permitidas:

std::cout << static_cast<MyString>(5); 

A inicialização direta ou uniforme também converterá os parâmetros para correspondência.

MyString str('x');

Em C++ 11, a palavra-chave explicit também pode ser usada com operadores de conversão.

fonte: https://www.learncpp.com