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";
}
Foi útil?

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 &param) 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.

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