Assisti a um vídeo do Scott Meyers sobre referências universais e ele disse que sempre que você usar uma referência universal, deverá encaminhá-la assim:
template<typename T>
auto func(T &&a) { return ++std::forward<T>(a); }
Então deduzindo this
temos:
auto member_func(this auto &&self) { return self.member_variable; }
Onde está o std::forward<???>(self).member_variable
aqui? O que eu perdi?
Geralmente a referência de encaminhamento no parâmetro de objeto explícito deve ser encaminhada como qualquer outra referência de encaminhamento:
Em geral não há nada diferente aqui de outras referências de encaminhamento. Você não informou de onde obteve o código, então não sei dizer por que o autor decidiu não encaminhar ou se é um problema neste caso específico.
No entanto, como este é o parâmetro de objeto explícito de uma função membro, normalmente você pode assumir qual tipo
self
terá, ao contrário da maioria das referências de encaminhamento em modelos de função gerais. Será o tipo da classe em que está escrito ou um tipo de classe derivado.Nesse caso, é razoável supor que
member_variable
se referirá ao membromember_variable
declarado na mesma classe. E você sabe que tipo esse membro tem.Agora suponha que seja assim:
Então, o tipo de
self.member_variable
pode ser razoavelmente assumido como sendoint
e, paraint
isso, não faz absolutamente nenhuma diferença se ele é retornado por valor (auto
) de uma expressão lvalue ou rvalue. Astd::forward
chamada seria conhecida por não ter efeito.Problemas com isso:
A função membro não está restrita ao tipo de classe em que ela aparece. Se o tipo de classe não estiver marcado
final
ou a função de membro declaradaprivate
, então será possível que ela seja chamada em um tipo de classe derivado que também defina ummember_variable
tipo diferente. Isso seria encontradoreturn self.member_variable;
e poderia ter um tipo em que o encaminhamento pudesse fazer a diferença, por exemplostd::string
.Porém, não é óbvio para mim se o autor do código considerou esse cenário, ou se eles realmente desejam sempre retornar o valor
member_variable
da própria classe. Nesse caso deveria serself.A::member_variable
ou algo assim. Ainda existem problemas com herança múltipla e herança privada nesse caso. (No entanto, existe uma solução para herança privada.)Se a referência de encaminhamento não for encaminhada, não há sentido em ter o parâmetro de objeto explícito
this auto&&
neste caso. Poderia simplesmente ser declarado comothis const auto&
outhis const A&
, o que também evitaria o problema mencionado acima. Na verdade, poderia ser simplesmente uma função de membro não estática normal, sem parâmetro de objeto explícito.Como ponteiros de função para funções-membro explícitas podem ser formados, é possível chamar a função-membro indiretamente por meio de tal ponteiro com qualquer outro tipo for
self
. Da mesma forma, uma conversão implícita para qualquer tipo pode ser forçada especificando explicitamente o argumento do modelo para oauto
tipo. No entanto, estes não são casos de uso normais e a função provavelmente não precisa considerá-los.Além disso, em geral, é importante lembrar o que
std::forward
significa usar. Aplicar ingenuamentestd::forward
a qualquer aparência de uma variável que seja uma referência de encaminhamento é errado. Issostd::forward
faz com que a expressão à qual é operando consuma (potencialmente) o estado do objeto. Geralmente só pode ser aplicado uma vez no corpo da função e depois o objeto não deve mais ser usado.No caso de uma expressão de acesso de membro como
é um pouco diferente: aqui
std::forward
provoca consumo potencial apenas domember_variable
membro. Os membros restantes ainda têm a garantia de estar em seu estado anterior. Portanto, é possível se inscreverstd::forward
uma vez para cada membro individualmente.