Pergunta

Por que é impossível ter uma referência ao vazio? A única coisa que eu encontrei no padrão C ++ é esta linha, a 8.3.2.1

A declarator que especifica o tipo "referência a cv vazio" está mal-formado.

Por que é assim? Por que não posso escrever uma função "genérico" que aceitar um void&?

Apenas para ser claro, eu não tenho nenhuma aplicação útil em mente que usando uma referência-a-vazio poderia ser melhor do que usar modelos, mas eu sou apenas curioso sobre a razão para proibir esta construção.


Para esclarecer um pouco, eu entendo que o uso de uma referência-a-vazio "como está" seria tão sem sentido quanto dereferencing um ponteiro-para-vazio. No entanto, eu poderia lançá-lo a uma referência-to- sometype , a fim de usá-lo, não podia? Na verdade, eu não vejo porque o seguinte trecho pode trabalhar ...

void foo(void *data)
{
    int *i = reinterpret_cast<int*>(data);
    // do something with i
}

... enquanto este não pode:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}
Foi útil?

Solução

Se você tinha uma referência ao vazio, o que você faria com ele? Não seria um número, ou um personagem, ou um ponteiro, ou qualquer coisa assim. Sua função genérica hipotética não poderia realizar qualquer operação sobre ele, exceto tomando seu endereço (e não o seu tamanho).

"vazio" tem dois usos: para isentar qualquer conhecimento do tipo (como em void *), e especificar nada em oposição a algo (retorno função void). Em nenhum dos casos é possível dizer nada sobre um vazio algo exceto que ele pode ter um endereço.

Se você não pode pensar de uma maneira algo pode ser útil, e eu não posso, que é pelo menos evidência de que algo é inútil, e que pode muito bem ser, pelo menos, parte da lógica aqui.

Outras dicas

Pergunte a si mesmo em primeiro lugar, como é que você de-referência a um ponteiro nulo?

void *p = /*something*/ ;
cout << *p << endl;

O código acima é sem sentido, uma das razões que temos vazio é para que possamos dizer "eu preciso fazer algum trabalho de ponteiro genérico aqui, e eu não conhecem nem ligo para o que eu estou apontando para". Por definição, o compilador não sabe o que um void * aponta, portanto, não pode excluir a referência dele. Você pode - por vazamento -. Mas o compilador não pode

Uma referência a um vazio sufferes do mesmo problema, por definição, os dados apontaram para não ter um tipo, portanto não pode ser referenciado em qualquer forma significativa.

Para fazer referência a ele você - o programador - necessidade de lançá-lo para outro tipo, então você pode ter uma referência digitado a ele

.

Não tenho certeza se eu expliquei isso tão bem quanto eu queria.

Ruben, todos os pensamentos?

EDIT:. Para responder a sua edição

Tome a primeira função, onde você passar dados void *. dados é um item perfeitamente válido, você pode calcular com ele, ou se você tiver alguns log implementado, você pode registrá-lo.

logger << data;

e você terá os pontos de dados de endereços para. Se você tentar dados dereference, o compilador lhe dará um erro (não tem compilador C ++ calhar momento, então não tenho certeza do erro real). por exemplo.

void* data = /* some assignment */;
logger << *data; // compiler error.

Agora, o compilador não vai deixar você cancelar a referência a * nula por qualquer razão (não faz sentido), os mesmos stands para uma referência ao vazio e dados, exceto que porque é uma referência é dereferenced implicitamente o tempo todo . O compilador não vai deixar você excluir a referência a * vazio em uma operação, ele não vai deixar você desreferenciava constantemente.

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

Você não pode fazer NADA para dados Except levá-la de endereço, e não há um perfeitamente bom - e seguro - método embutido no languge de fazer isso, ou seja

void* data;

É este fazendo mais sentido?

Uma referência é uma referência a uma instância de alguma coisa. Um exemplo de algo que não pode ser do tipo void. Qualquer instância de algo deve ter um tipo específico (e tipos possivelmente de base).

Aqui está um resumo das coisas diferentes que têm sido dito, e que eu tenho pensado.

Duas razões principais pelas quais referência-a-nulos não são permitidos


1 Eles teriam sido totalmente inútil.

De fato, se olharmos para os tempos de C, ponteiros void tinha duas finalidades:

  • Gerenciamento de memória (por exemplo malloc)
  • Genericity (funções que podem aceitar qualquer tipo de argumentos escrever)

Quando C ++ saiu, modelos tornou-se a melhor solução para implementar genericidade. No entanto, o gerenciamento de memória costume ainda tinha que ser possível, e inter-operabilidade entre C ++ e C foi uma grande preocupação, então void * foi keeped. Uma referência nula hipotético seria de nenhuma ajuda com gerenciamento de memória e genericidade já está coberto, então basicamente ela teria quase nenhum uso (exceto para a garantia de não-nullness descrito abaixo).

2 Você não seria capaz de fazer qualquer coisa com ele

Ao usar um ponteiro nulo, você não está autorizado a excluir a referência-lo; transposta para o caso de referências, isso significa que você não pode usar a referência void (sempre hipotético). Então

void *data = // something
// using *data and data-> is forbidden

void &data = // something
// using data is forbidden

No entanto, poderíamos pensar em um caso de uso onde a referência não teria que ser "desreferenciado" (essa frase é terrivelmente errado, mas você começa o meu ponto), mas em que só iria tomar seu endereço. Vamos supor que eu tenho a seguinte função:

void foo(void *dataptr)
{
    assert(dataptr != NULL); // or != 0
    // do something with dataptr
}

Para evitar esse assert chato, eu poderia escrever a função desta forma:

void foo(void &dataref)
{
    void *data = &dataref;
    // do something with data
}

No entanto, para que isso funcione, necessidades &dataref para ser equivalente a dataptr, que não é o caso : &dataref é equivalente a &*dataptr!

Por isso, mesmo tendo o endereço implica uma dereferencing, pelo menos conceitualmente (nos bastidores, a primeira equivalência é provavelmente verdade, mas ao nível semântico não é). Consequentemente, não há absolutamente nenhum uso que podemos fazer de dados, de modo referências vazios são uma aberração.

Tecnicamente falando, tudo o que é garantido é que uma referência a um objeto é um apelido para ele. Que sob o capô argumento referência de passagem é feito com ponteiros é um detalhe de implementação. Isso pode ser confuso por causa das referências reutilizando o operador & que também é o endereço-of, mas tenha em mente que o operador realmente tem significados diferentes em contextos diferentes (em uma declaração de variável ou parâmetro que denota um tipo de referência, caso contrário, de endereço-de , exceto quando é bit a bit-e). Porque é tecnicamente apenas um alias para um objeto, uma referência é 'sempre desreferenciado', como explicou Worrier.

OK, uma coisa está me incomodando sobre isso. A idéia de um void*, como mencionado acima, é que você ainda tem uma variável válido, contendo um endereço, mas o tipo está sendo ignorada. Isto parece permitida uma vez que ainda pode trabalhar com os dados de endereços - o tipo é um pouco supérfluo (ou menos importante) neste contexto. Dereferencing é ruim, porque tentar e acesso um membro não faz sentido, por exemplo, p.mem. Não sabemos qual classe para se referir a, e, portanto, a memória para saltar para, os ponteiros vtable a seguir.

No entanto, seria então parece fazer sentido que p por conta própria seria OK uma vez que só iria se referir ao objeto, mas nenhum de seus dados. Sem informações de classe é necessária para fazê-lo, apenas o endereço. Eu entendo que não há absolutamente nenhum uso para isso, mas é importante para definir quando as coisas quebram. Permitindo que esta noção, a ++ referência C (constantemente desreferenciado, mas não aceder a qualquer coisa) por exemplo void& ref = static_cast< &void >(obj) também faz sentido e, portanto, permitiria referências vazios. Não estou dizendo que ninguém deve tomar-se com os responsáveis, mas de um ponto de vista "fazer sentido", seria parece correta, não?

Como Luc Touraille apontado acima (pelo menos, esta é a minha interpretação), poderia ser implementada, mas o assunto é semântica. A explicação razoável que eu poderia vir para era que uma vez que uma variável de objeto é uma "tag" para uma sequência de memória, o tipo é de importante valor semântico. Assim, o ponteiro, que está sendo pensado como uma variável com um valor de endereço, trata do tipo como algo supérfluo -. Não chave para defini-lo

que alguém iria concordar com isso?

Você pode pensar em uma referência como um ponteiro-referenciada de. Sintaticamente você tratar uma referência como se não é um ponteiro: você não precisa do operador * excluir a referência-lo, e você pode usar. ao invés de -.> para acessar seus membros

No entanto, você não pode excluir a referência um ponteiro void. Como fora apontado por Binary Worrier tentando fazer que lhe dará um erro do compilador. E se você não pode ter um ponteiro nulo desreferenciado, isso significa que você não pode ter uma referência nula.

Se fossem, eles seriam semanticamente não-diferenciada de ponteiros, e equivaleria a açúcar sintático. A referência diz: "refiro-me a algo que é desse tipo." Permitindo referência nula ou nulo enfraqueceria essa diferença de ponteiros.

Com certeza, ainda é possível para uma referência para se referir a um objeto que não existe mais, mas isso é uma exceção.

O seguinte é não a defesa da noção de referências vazios. Eu oferecê-lo como uma anedota na natureza. Pergunte a si mesmo se ele não cheira engraçado.

Minha empresa foi uma das primeiras usando C ++ comercialmente, e inicialmente compilado usando Cfront . Os primeiros desenvolvedores ainda estavam aprendendo a língua, e geralmente usando cada truque no livro (operadores em todos os lugares!). Aqui está um truque que eles pensavam era legal:

void Foo::something(int action, ostream &os = *(ostream *)0)
{
   ostream *os_p = &os;
   if (&os == (ostream *)0) {
      os_p = &cerr;
   }
   // continue with method
}

Então, aqui você tem, não uma referência nula, mas sim uma referência digitado com um potencialmente vazio obrigatório! Um momento de reflexão provavelmente deve sugerir alternativas melhores para esse idioma em particular ...

vazio é algo que, por definição, não existe, por isso não é lógico para tê-lo de endereço.

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