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?

Foi útil?

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 ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top