C ++ Diferença entre a conversão do tipo automático para std :: string e char*
-
22-07-2019 - |
Pergunta
Como exercício de aprendizado, tenho analisado como a conversão do tipo automático funciona no C ++. EU conhecer Essa conversão do tipo automático geralmente deve ser evitado, mas eu gostaria de aumentar meu conhecimento de C ++, entendendo como ele funciona de qualquer maneira.
Eu criei um StdStringConverter
classe que pode ser convertida automaticamente para um std::string
, mas o compilador (G ++ 4.3.4 no Debian) parece não fazer a conversão quando o objeto é comparado com um real std::string
(Por favor, ignore a falta de passagem por referência e criação desnecessária de objetos temporários):
#include <string>
class StdStringConverter
{
public:
explicit StdStringConverter(std::string name) : m_name(name) {}
operator const std::string () const { return m_name; }
private:
std::string m_name;
};
int main()
{
StdStringConverter converter(std::string("Me"));
const std::string name = "Me";
// Next line causes compiler error:
// no match for 'operator==' in 'converter == name'
return (converter == name) ? 0 : 1;
}
Por outro lado, se eu mudar um pouco para um CStringConverter
classe, a conversão automática faz acontecer, apesar de comparar char
Ponteiros provavelmente não são o que eu pretendia:
#include <string>
class CStringConverter
{
public:
explicit CStringConverter(std::string name) : m_name(name) {}
operator const char* () const { return m_name.c_str(); }
private:
std::string m_name;
};
int main()
{
CStringConverter converter(std::string("Me"));
const char* name = "Me";
// Next line compiles fine, but they are not equal because the
// pointers don't match.
return (converter == name) ? 0 : 1;
}
Existe algo especial na diferença entre um std::string
e a char*
Nesse contexto, que faz com que o compilador não os trate da mesma forma?
Solução
O problema se deve ao fato de a string std :: é realmente uma instância do modelo de classe std :: basic_string. Um operador == que está disponível no espaço de nome STD leva dois modelos de std :: basic_string:
template<class charT, class traits, class Allocator>
bool operator==(const basic_string& lhs,
const basic_string& rhs);
Se esta versão do operador == foi sobrecarregada especificamente em std :: string, seu código ficaria bem. Mas esse não é o caso, o que exigiria que o compilador executasse dedução do argumento do modelo nos parâmetros de modelo do std :: Basic_String para que pudesse entender que o retorno do seu operador de conversão é uma correspondência possível.
No entanto, o compilador não fará isso. Não sei qual parte do padrão declara com exatamente. Mas a idéia geral é que essas conversões funcionem apenas para tipos de não -ora.
Uma coisa que posso sugerir é que você coloque o STDSTRINGCONVERTER em um espaço para nome e forneça uma versão do operador == para std :: string nesse espaço de nome. Dessa forma, quando seu compilador encontra uma expressão como a ADL (pesquisa dependente de argumentos) entra em jogo e tudo funciona bem.
#include <string>
namespace n1 {
class StdStringConverter
{
public:
explicit StdStringConverter(std::string name) : m_name(name) {}
operator std::string () { return m_name; }
private:
std::string m_name;
};
bool operator==(std::string const& a, std::string const& b)
{
return a == b; //EDIT: See Paul's comment on std::operator== here.
}
}
int main()
{
using namespace n1;
StdStringConverter converter(std::string("Me"));
std::string name = "Me";
return (converter == name) ? 0 : 1;
}
Outras dicas
No primeiro exemplo, as duas classes comparadas (String e StdstringConverter) não recebem nenhum tratamento especial do compilador para a conversão do tipo. Isso significa que a sobrecarga do operador que você fez nem é acionada. O compilador olha através da lista de sobrecarga do operador == e não delas apreciam um STDSTRINGCONVERTER para que ele grite com você.
No segundo exemplo, o nome é char *. Como é um tipo primitivo, o compilador tenta converter o não primitivo em um char *. Como você tem uma substituição no local, ela funciona e compara endereços.
O compilador não terá o tipo explícito de operações que não incluem tipos primitivos. Algo que fará é tentar usar construtores para fazer com que os tipos correspondam. Por exemplo, se você mudar seu primeiro exemplo para isso:
#include <string>
class StdStringConverter
{
public:
StdStringConverter(std::string name) : m_name(name) {}
bool operator==(const StdStringConverter &name) { return m_name == name.m_name; }
operator const std::string () const { return m_name; }
private:
std::string m_name;
};
int main()
{
StdStringConverter converter(std::string("Me"));
const std::string name = "Me";
// Next line causes compiler error:
// no match for 'operator==' in 'converter == name'
return (converter == name) ? 0 : 1;
}
Agora, o programa retorna 0. Como o construtor agora não está explícito, o compilador tentará usá -lo para converter a string em um STDSTRINGCONVERTER. Como agora existe um operador == no STDSTRINGCONVERTER, tudo funciona.
Existem vários fatores. Se você alterar a declaração de retorno assim
return (std :: operator == (nome, nome))? 0: 1;
Ele compila, embora obviamente não faça a mesma coisa. Por outro lado
return (std :: operator == (conversor, nome))? 0: 1;
não fornece, mas fornece uma mensagem de erro mais interessante
Nenhuma função de correspondência para Call to 'Operator == (StdstringConverter &, const std :: string &)
O que me lembra que o operador == é modificado no Basic_String <>, que possui três parâmetros de modelo para inicializar. Se você usa int em seu exemplo em vez de std :: string, o compilador não reclama.
Como obter o efeito desejado com std :: string é mais intrigante ...