Não foi possível encontrar operador através de conversão implícita em C ++
-
05-09-2019 - |
Pergunta
Ao escrever uma classe para atuar como um invólucro em torno de um objeto alocado-heap, I encontrou um problema com a conversão de tipo implícito que pode ser reduzido a esse exemplo simples.
No código abaixo da classe wrapper gerencia um objeto alocado-heap e implicitamente converte em uma referência a esse objeto. Isso permite que o objeto wrapper para ser passado como argumento para a gravação de função (...) desde conversão implícita ocorre.
O compilador falhar, no entanto, ao tentar resolver a chamada para o operador << (...), a menos que uma conversão explícita é feita (marcada com MSVC8.0, Intel 9.1 e gcc 4.2.1 compiladores).
Assim, (1) por que a conversão implícita falhar neste caso? (2) que poderia estar relacionado com pesquisa dependentes do argumento? e (3) não há nada que pode ser feito para fazer este trabalho sem a conversão explícita?
#include <fstream>
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
void write(std::ostream& os)
{
os << "(1) Hello, world!\n";
}
int main()
{
wrapper<std::ostream> file(new std::ofstream("test.txt"));
write(file);
static_cast<std::ostream&>( file ) << "(2) Hello, world!\n";
// file << "(3) This line doesn't compile!\n";
}
Solução
Ele falha porque você está tentando resolver um operador de sua classe wrapper<T>
que não existe. Se você quer que ele funcione sem o elenco, você poderia montar algo como isto:
template<typename X> wrapper<T> &operator <<(X ¶m) const {
return t << param;
}
Infelizmente eu não sei de uma maneira de resolver o tipo de retorno em tempo de compilação. Felizmente, na maioria dos casos é do mesmo tipo que o objeto, incluindo neste caso com ostream
.
EDIT: Código Modificado por sugestão de traço-tom-bang. Changed tipo de retorno para wrapper<T> &
.
Outras dicas
O compilador não tem contexto suficiente para determinar que operator&
vai fazer uma conversão válida. Então, sim, eu acho que isso está relacionado com a pesquisa dependentes do argumento:. O compilador está procurando um operator<<
que pode aceitar um const
não wrapper<std::ostream>
como seu primeiro parâmetro
Eu acho que o problema tem a ver com a manutenção de algumas restrições de tempo de compilação. No seu exemplo, o compilador primeiro teria que encontrar todos o operador possível <<. Então, para cada um deles, ele deve tentar se o seu objeto pode ser convertido automaticamente (direta ou indiretamente) para qualquer um dos tipos que cada operador << é capaz de aceitar.
Este teste pode ser muito complexo, e eu acho que isso é restrito a fornecer um tempo de compilação razoável.
Depois de alguns testes, um exemplo ainda mais simples identifica a origem do problema. O compilador não pode deduzir a T
argumento modelo no f2(const bar<T>&)
abaixo de conversão implícita de wrapper<bar<int> >
para bar<int>&
.
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
class foo { };
template <typename T> class bar { };
void f1(const foo& s) { }
template <typename T> void f2(const bar<T>& s) { }
void f3(const bar<int>& s) { }
int main()
{
wrapper<foo> s1(new foo());
f1(s1);
wrapper<bar<int> > s2(new bar<int>());
//f2(s2); // FAILS
f2<int>(s2); // OK
f3(s2);
}
No exemplo original, std::ostream
é realmente um typedef
para o std::basic_ostream<..>
classe de modelo, ea mesma situação se aplica ao chamar o operator<<
função templated.
Verifique a assinatura do operador de inserção ... Eu acho que eles tomam referência ostream não-const?
Confirmado com C ++ 03 standard, assinatura do char * operador de saída é:
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
que, de fato, ter uma referência não-const. Portanto, o seu operador de conversão não corresponde.
Como observado no comentário:. Isso é irrelevante
Existem vários limites no padrão sobre conversões aplicado ... talvez isso seria necessário para conversões implícitas (o seu operador, e elenco com o tipo de base), quando no máximo uma deve ser aplicada.