Quero entrar em mais meta-programação de modelos. Eu sei que SFINAE significa "falha na substituição não é um erro". Mas alguém pode me mostrar um bom uso da SFINAE?
c++
templates
metaprogramming
sfinae
rlbond
fonte
fonte
Respostas:
Aqui está um exemplo ( daqui ):
Quando
IsClassT<int>::Yes
é avaliado, 0 não pode ser convertido emint int::*
porque int não é uma classe e, portanto, não pode ter um ponteiro de membro. Se o SFINAE não existisse, você obteria um erro do compilador, algo como '0 não pode ser convertido em ponteiro de membro para o tipo não pertencente à classe'. Em vez disso, ele apenas usa o...
formulário que retorna Dois e, portanto, é avaliado como falso, int não é um tipo de classe.fonte
...
, mas aint C::*
, que eu nunca tinha visto e tive que procurar. Encontrei a resposta para o que é e para o que pode ser usado aqui: stackoverflow.com/questions/670734/…Eu gosto de usar
SFINAE
para verificar condições booleanas.Pode ser bastante útil. Por exemplo, usei-o para verificar se uma lista de inicializadores coletada usando vírgula do operador não tem mais que um tamanho fixo
A lista é aceita apenas quando M é menor que N, o que significa que a lista de inicializadores não possui muitos elementos.
A sintaxe
char(*)[C]
significa: Ponteiro para uma matriz com o tipo de elemento char e sizeC
. SeC
for falso (0 aqui), obtemos o tipo inválidochar(*)[0]
, ponteiro para uma matriz de tamanho zero: SFINAE faz com que o modelo seja ignorado.Expressado com
boost::enable_if
, que se parece com issoNa prática, muitas vezes considero a capacidade de verificar as condições uma habilidade útil.
fonte
M <= N ? 1 : -1
funcione.int foo[0]
. Não estou surpreso com o suporte, pois permite o truque "estrutura que termina com uma matriz de 0 comprimento" muito útil ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).error C2466: cannot allocate an array of constant size 0
No C ++ 11, os testes SFINAE tornaram-se muito mais bonitos. Aqui estão alguns exemplos de usos comuns:
Escolha uma sobrecarga de função dependendo das características
Usando um idioma de coletor de tipo, você pode fazer testes bastante arbitrários em um tipo como verificar se ele tem um membro e se esse membro é de um determinado tipo
Aqui está um exemplo ao vivo: http://ideone.com/dHhyHE Também recentemente escrevi uma seção inteira sobre SFINAE e envio de tags no meu blog (plug descarado, mas relevante) http://metaporky.blogspot.de/2014/08/ part-7-static-dispatch-function.html
Observe que no C ++ 14 existe um std :: void_t que é essencialmente o mesmo que o meu TypeSink aqui.
fonte
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
em um lugar e depoisTypeSinkT<decltype(&T::bar)>
em outro? Também é&
necessário emstd::declval<T&>
?TypeSink
, C ++ 17 têmstd::void_t
:)A biblioteca enable_if do Boost oferece uma interface limpa e agradável para o uso do SFINAE. Um dos meus exemplos de uso favoritos está na biblioteca Boost.Iterator . SFINAE é usado para habilitar conversões do tipo iterador.
fonte
O C ++ 17 provavelmente fornecerá um meio genérico para consultar recursos. Consulte N4502 para obter detalhes, mas como um exemplo independente, considere o seguinte.
Esta parte é a parte constante, coloque-a em um cabeçalho.
O exemplo a seguir, retirado do N4502 , mostra o uso:
Comparado com as outras implementações, esta é bastante simples: basta um conjunto reduzido de ferramentas (
void_t
edetect
). Além disso, foi relatado (consulte a N4502 ) que é mensuravelmente mais eficiente (tempo de compilação e consumo de memória do compilador) do que as abordagens anteriores.Aqui está um exemplo ao vivo , que inclui ajustes de portabilidade para o GCC pré 5.1.
fonte
Aqui está outra (tarde) SFINAE exemplo, com base em Greg Rogers 's resposta :
Dessa maneira, você pode verificar o
value
valor de para ver seT
é uma classe ou não:fonte
int C::*
na sua resposta significa? Como podeC::*
ser um nome de parâmetro?int C::*
é o tipo de um ponteiro para umaint
variável de membro deC
.Aqui está um bom artigo do SFINAE: Uma introdução ao conceito SFINAE do C ++: introspecção em tempo de compilação de um membro da classe .
Resuma da seguinte forma:
declval
é um utilitário que fornece uma "referência falsa" a um objeto de um tipo que não pode ser facilmente construído.declval
é realmente útil para nossas construções SFINAE.fonte
Aqui, estou usando a sobrecarga de função de modelo (não diretamente SFINAE) para determinar se um ponteiro é uma função ou ponteiro de classe de membro: ( É possível corrigir os ponteiros de função de membro costro / cerr iostream que estão sendo impressos como 1 ou verdadeiro? )
https://godbolt.org/z/c2NmzR
Impressões
Como o código é, ele pode (dependendo do compilador "bom") gerar uma chamada em tempo de execução para uma função que retornará verdadeiro ou falso. Se você deseja forçar a
is_function_pointer(var)
avaliação no tipo de compilação (nenhuma chamada de função é executada em tempo de execução), você pode usar oconstexpr
truque de variável:Pelo padrão C ++, todas as
constexpr
variáveis são garantidas para serem avaliadas em tempo de compilação ( comprimento de computação de uma cadeia C em tempo de compilação. Isso é realmente um constexpr? ).fonte
O código a seguir usa SFINAE para permitir que o compilador selecione uma sobrecarga com base no fato de um tipo ter determinado método ou não:
Resultado:
fonte