Atualização: no C ++ 11, pode-se usar em std::addressof
vez de boost::addressof
.
Vamos primeiro copiar o código do Boost, menos o trabalho do compilador em torno dos bits:
template<class T>
struct addr_impl_ref
{
T & v_;
inline addr_impl_ref( T & v ): v_( v ) {}
inline operator T& () const { return v_; }
private:
addr_impl_ref & operator=(const addr_impl_ref &);
};
template<class T>
struct addressof_impl
{
static inline T * f( T & v, long ) {
return reinterpret_cast<T*>(
&const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
}
static inline T * f( T * v, int ) { return v; }
};
template<class T>
T * addressof( T & v ) {
return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
}
O que acontece se passarmos uma referência à função ?
Nota: addressof
não pode ser usado com um ponteiro para funcionar
Em 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, ambos T &
e T *
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 do f
, o qual é resolvido graças ao argumento manequim 0
, que é um int
primeiro e pode ser promovido a um long
(Conversão integral).
Assim, simplesmente retornamos o ponteiro.
O que acontece se passarmos um tipo com um operador de conversão?
Se o operador de conversão T*
gerar um, teremos uma ambiguidade: para f(T&,long)
uma Promoção Integral é necessária para o segundo argumento, enquanto para f(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 tipo addr_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).
O que acontece com qualquer outro tipo?
Assim, a f(T&,long)
sobrecarga é selecionada, porque o tipo não corresponde ao T*
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.
O que acontece nessa sobrecarga?
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
e volatile
qualificadores para evitar avisos do compilador (e use corretamente a const_cast
para removê-los).
- Transmitir
T&
parachar const volatile&
- Retire o
const
evolatile
- Aplique o
&
operador ao endereço
- Lançar de volta para um
T*
O const
/ volatile
malabarismo é um pouco de magia negra, mas simplifica o trabalho (em vez de fornecer 4 sobrecargas). Observe que, uma vez que T
não é qualificado, se passarmos a ghost const&
, então T*
é 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.