Considere o seguinte programa:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
Como obtenho clyde
o endereço de?
Estou procurando uma solução que funcione igualmente bem para todos os tipos de objetos. Uma solução C ++ 03 seria legal, mas também estou interessado em soluções C ++ 11. Se possível, vamos evitar qualquer comportamento específico da implementação.
Estou ciente do std::addressof
modelo de função do C ++ 11 , mas não estou interessado em usá-lo aqui: gostaria de entender como um implementador da Biblioteca Padrão pode implementar esse modelo de função.
c++
c++11
operator-overloading
memory-address
James McNellis
fonte
fonte
:)
)CComPtr<>
eCComQIPtr<>
ter um sobrecarregadooperator&
Respostas:
Atualização: no C ++ 11, pode-se usar em
std::addressof
vez deboost::addressof
.Vamos primeiro copiar o código do Boost, menos o trabalho do compilador em torno dos bits:
Nota:
addressof
não pode ser usado com um ponteiro para funcionarEm C ++, se
void func();
declarado,func
é uma referência a uma função que não aceita argumentos e não retorna resultados. Essa referência a uma função pode ser trivialmente convertida em um ponteiro para a função - de@Konstantin
: De acordo com 13.3.3.2, ambosT &
eT *
são indistinguíveis para funções. O primeiro é uma conversão de Identidade e o segundo é a conversão de Função em Ponteiro, ambos com a classificação "Correspondência Exata" (13.3.3.1.1 tabela 9).A referência a função passar através
addr_impl_ref
, existe uma ambiguidade na resolução de sobrecarga para a escolha dof
, o qual é resolvido graças ao argumento manequim0
, que é umint
primeiro e pode ser promovido a umlong
(Conversão integral).Assim, simplesmente retornamos o ponteiro.
Se o operador de conversão
T*
gerar um, teremos uma ambiguidade: paraf(T&,long)
uma Promoção Integral é necessária para o segundo argumento, enquanto paraf(T*,int)
o operador de conversão é chamado no primeiro (graças a @litb)É
addr_impl_ref
aí que entra em ação. O Padrão C ++ exige que uma sequência de conversão possa conter no máximo uma conversão definida pelo usuário. Ao envolver o tipoaddr_impl_ref
e forçar o uso de uma sequência de conversão, "desabilitamos" qualquer operador de conversão fornecido com o tipo.Assim, a
f(T&,long)
sobrecarga é selecionada (e a Promoção Integral é realizada).Assim, a
f(T&,long)
sobrecarga é selecionada, porque o tipo não corresponde aoT*
parâmetro.Nota: a partir das observações no arquivo sobre compatibilidade com a Borland, as matrizes não decaem para ponteiros, mas são passadas por referência.
Queremos evitar a aplicação
operator&
ao tipo, pois ele pode ter sido sobrecarregado.A Norma garante que
reinterpret_cast
pode ser usada para este trabalho (consulte a resposta de @Matteo Italia: 5.2.10 / 10).O Boost adiciona alguns detalhes
const
evolatile
qualificadores para evitar avisos do compilador (e use corretamente aconst_cast
para removê-los).T&
parachar const volatile&
const
evolatile
&
operador ao endereçoT*
O
const
/volatile
malabarismo é um pouco de magia negra, mas simplifica o trabalho (em vez de fornecer 4 sobrecargas). Observe que, uma vez queT
não é qualificado, se passarmos aghost const&
, entãoT*
éghost const*
, portanto os qualificadores não foram realmente perdidos.EDIT: a sobrecarga do ponteiro é usada para ponteiro para funções, eu alterei a explicação acima um pouco. Ainda não entendo por que é necessário .
A seguinte saída de ideone resume isso, um pouco.
fonte
f
sobrecargas onde funcionavam modelos, enquanto que elas são funções membro regulares de uma classe de modelo, obrigado por apontá-las. (Agora eu só preciso descobrir o que é o uso da sobrecarga, qualquer dica?)char*
". Obrigado, Matthieu.T*
? EDIT: Agora eu vejo. Seria, mas, com o0
argumento, acabaria entrecruzado , seria ambíguo.Use
std::addressof
.Você pode pensar nisso como fazer o seguinte nos bastidores:
As implementações existentes (incluindo o Boost.Addressof) fazem exatamente isso, apenas tomando cuidado
const
evolatile
qualificação adicionais.fonte
O truque por trás
boost::addressof
e a implementação fornecida por @Luc Danton depende da mágica doreinterpret_cast
; o padrão declara explicitamente em §5.2.10 ¶10 queAgora, isso nos permite converter uma referência arbitrária de objeto para a
char &
(com uma qualificação cv se a referência for qualificada para cv), porque qualquer ponteiro pode ser convertido em uma (possivelmente qualificado para cv)char *
. Agora que temos umchar &
, a sobrecarga do operador no objeto não é mais relevante e podemos obter o endereço com o&
operador interno .A implementação do boost adiciona algumas etapas para trabalhar com objetos qualificados para cv: a primeira
reinterpret_cast
é feita paraconst volatile char &
, caso contrário, uma conversão simpleschar &
não funcionaria paraconst
e / ouvolatile
referências (reinterpret_cast
não é possível removerconst
). Em seguida, oconst
evolatile
é removido comconst_cast
, o endereço é levado com&
e uma finalreinterpet_cast
para o tipo "correto" é feita.A
const_cast
é necessária para remover oconst
/volatile
que poderia ter sido adicionado ao não-const / referências voláteis, mas ele não "prejudicar" o que era umconst
/volatile
referência em primeiro lugar, porque a finalreinterpret_cast
vai voltar a adicionar a cv-qualificação se fosse lá em primeiro lugar (reinterpret_cast
não é possível remover oconst
mas pode adicioná-lo).Quanto ao restante do códigoaddressof.hpp
, parece que a maior parte é para soluções alternativas. Ostatic inline T * f( T * v, int )
parece ser necessária apenas para o compilador Borland, mas sua presença introduz a necessidade deaddr_impl_ref
, caso contrário, tipos de ponteiro seria pego por esta segunda sobrecarga.Edit : as várias sobrecargas têm uma função diferente, consulte @Matthieu M. excelente resposta .Bem, também não tenho mais certeza disso; Eu deveria investigar mais esse código, mas agora estou preparando o jantar :), vou dar uma olhada mais tarde.
fonte
void func();
boost::addressof(func);
. No entanto, remover a sobrecarga não impede o gcc 4.3.4 de compilar o código e produzir a mesma saída, então ainda não entendo por que é necessário ter essa sobrecarga.Eu vi uma implementação de
addressof
fazer isso:Não me pergunte como isso é compatível!
fonte
char*
é a exceção listada para digitar regras de alias.reinterpret_cast<char*>
bem definido.[unsigned] char *
e, assim, ler a representação do objeto apontado. Essa é outra área em quechar
há privilégios especiais.Dê uma olhada no boost :: addressof e sua implementação.
fonte
addressof
retorna o ponteiro em si. É discutível se é o que o usuário queria ou não, mas é como ele especificou.addr_impl_ref
, de modo a sobrecarga ponteiro nunca deve ser chamado ...