Scott Meyers publicou o conteúdo e o status de seu próximo livro EC ++ 11. Ele escreveu que um item do livro poderia ser "Evitar std::enable_if
assinaturas de funções" .
std::enable_if
pode ser usado como argumento de função, como tipo de retorno ou como modelo de classe ou parâmetro de modelo de função para remover condicionalmente funções ou classes da resolução de sobrecarga.
Em esta questão todos os três solução são mostrados.
Como parâmetro de função:
template<typename T>
struct Check1
{
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, int>::value >::type* = 0) { return 42; }
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, double>::value >::type* = 0) { return 3.14; }
};
Como parâmetro do modelo:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if<
std::is_same<U, int>::value, int>::type = 0>
U read() { return 42; }
template<typename U = T, typename std::enable_if<
std::is_same<U, double>::value, int>::type = 0>
U read() { return 3.14; }
};
Como tipo de retorno:
template<typename T>
struct Check3
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
return 42;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
return 3.14;
}
};
- Qual solução deve ser preferida e por que devo evitar outras?
- Em que casos "Evitar
std::enable_if
assinaturas de função" refere-se ao uso como tipo de retorno (que não faz parte da assinatura normal da função, mas de especializações de modelo)? - Existem diferenças para os modelos de função membro e não membro?
std::enable_if
de bagunçar minhas assinaturas de funções (especialmente anullptr
versão feia de argumentos de funções adicionais ) porque sempre parece com o que é, um truque estranho (para algo questatic if
pode faça muito mais bonito e limpo) usando o template black-magic para explorar um recurso de linguagem interessante. É por isso que eu prefiro o envio de tags sempre que possível (bem, você ainda tem argumentos estranhos adicionais, mas não na interface pública e também muito menos feia e enigmática ).=0
emtypename std::enable_if<std::is_same<U, int>::value, int>::type = 0
realizar? Não consegui encontrar os recursos corretos para entendê-lo. Eu sei que a primeira parte anterior=0
tem um tipo de membroint
seU
eint
é o mesmo. Muito Obrigado!Respostas:
Coloque o hack nos parâmetros do modelo .
A
enable_if
abordagem do parâmetro on template possui pelo menos duas vantagens sobre as outras:legibilidade : o uso enable_if e os tipos de retorno / argumento não são mesclados em um pedaço confuso de desambiguadores de nomes de tipos e acessos de tipos aninhados; mesmo que a desordem do tipo desambiguador e aninhado possa ser atenuada com modelos de alias, isso ainda mesclaria duas coisas não relacionadas. O uso enable_if está relacionado aos parâmetros do modelo e não aos tipos de retorno. Tê-los nos parâmetros do modelo significa que eles estão mais próximos do que importa;
aplicabilidade universal : os construtores não têm tipos de retorno e alguns operadores não podem ter argumentos extras; portanto, nenhuma das outras duas opções pode ser aplicada em qualquer lugar. Colocar enable_if em um parâmetro de modelo funciona em qualquer lugar, pois você só pode usar SFINAE em modelos de qualquer maneira.
Para mim, o aspecto da legibilidade é o grande fator motivador dessa escolha.
fonte
FUNCTION_REQUIRES
macro aqui torna muito mais agradável a leitura e funciona também nos compiladores C ++ 03, e depende do usoenable_if
no tipo de retorno. Além disso, o uso deenable_if
parâmetros no modelo de função causa problemas de sobrecarga, porque agora a assinatura da função não é exclusiva, causando erros ambíguos de sobrecarga.enable_if
um parâmetro de modelo não-tipo padrão, que permite sobrecarga. Ou seja, emenable_if_t<condition, int> = 0
vez detypename = enable_if_t<condition>
.flamingdangerzone
link no seu comentário parece levar a uma página de instalação de spyware agora. Eu o sinalizei para atenção do moderador.std::enable_if
confia no princípio " Falha na substituição não é um erro " (também conhecido como SFINAE) durante a dedução do argumento do modelo . Esse é um recurso de linguagem muito frágil e você precisa ter muito cuidado para corrigi-lo.enable_if
contiver um modelo aninhado ou uma definição de tipo (dica: procure::
tokens), a resolução desses tempatles ou tipos aninhados geralmente será um contexto não deduzido . Qualquer falha de substituição em um contexto não deduzido é um erro .enable_if
sobrecargas não podem ter nenhuma sobreposição porque a resolução da sobrecarga seria ambígua. Isso é algo que você, como autor, precisa verificar por si mesmo, apesar de receber bons avisos do compilador.enable_if
manipula o conjunto de funções viáveis durante a resolução de sobrecarga, o que pode ter interações surpreendentes, dependendo da presença de outras funções trazidas de outros escopos (por exemplo, através de ADL). Isso o torna não muito robusto.Em resumo, quando funciona, funciona, mas quando não funciona, pode ser muito difícil depurar. Uma alternativa muito boa é usar o envio de tags , ou seja, delegar para uma função de implementação (geralmente em um
detail
espaço para nome ou em uma classe auxiliar) que recebe um argumento fictício com base na mesma condição de tempo de compilação usada naenable_if
.O envio de tags não manipula o conjunto de sobrecargas, mas ajuda a selecionar exatamente a função desejada, fornecendo os argumentos adequados por meio de uma expressão em tempo de compilação (por exemplo, em uma característica de tipo). Na minha experiência, isso é muito mais fácil de depurar e acertar. Se você é um aspirante a escritor de bibliotecas de características sofisticadas, pode precisar de
enable_if
alguma forma, mas para o uso mais regular de condições de tempo de compilação não é recomendado.fonte
enable_if
faria certo?is_f_able
é algo que considero uma tarefa para os escritores de bibliotecas que, obviamente, podem usar o SFINAE quando isso lhes dá uma vantagem, mas para usuários "regulares" e com uma característicais_f_able
, acho que o envio de tags é mais fácil.O parâmetro do modelo
Pode ser facilmente utilizado incorretamente e produz erros com sobrecargas:
Observe em
typename = std::enable_if_t<cond>
vez de corrigirstd::enable_if_t<cond, int>::type = 0
tipo de retorno:
Por último, no parâmetro de função:
+
,-
,*
, ...)void* = nullptr
) (para que o ponteiro da função seja diferente e assim por diante)Existem diferenças sutis com herança e
using
:De acordo com
using-declarator
(ênfase minha):namespace.udecl
Portanto, para o argumento do modelo e o tipo de retorno, os métodos estão ocultos no seguinte cenário:
Demo (o gcc encontra incorretamente a função base).
Considerando que com argumento, cenário semelhante funciona:
Demo
fonte