Estou usando fortemente a função SFINAE em um projeto e não tenho certeza se existem diferenças entre as duas abordagens a seguir (além do estilo):
#include <cstdlib>
#include <type_traits>
#include <iostream>
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
std::cout << "method 1" << std::endl;
}
template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
std::cout << "method 2" << std::endl;
}
int main()
{
foo<int>();
foo<double>();
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
A saída do programa é conforme o esperado:
method 1
method 2
Done...
Vi o método 2 usado com mais frequência no stackoverflow, mas prefiro o método 1.
Existem circunstâncias em que essas duas abordagens diferem?
Respostas:
Sugestão: prefira o método 2.
Ambos os métodos funcionam com funções únicas. O problema surge quando você possui mais de uma função, com a mesma assinatura, e deseja ativar apenas uma função do conjunto.
Suponha que você deseje ativar a
foo()
versão 1 quandobar<T>()
(fingir que é umaconstexpr
função)true
e afoo()
versão 2 quandobar<T>()
forfalse
.Com
você recebe um erro de compilação porque possui uma ambiguidade: duas
foo()
funções com a mesma assinatura (um parâmetro de modelo padrão não altera a assinatura).Mas a seguinte solução
funciona, porque SFINAE modifica a assinatura das funções.
Observação não relacionada: também existe um terceiro método: ativar / desativar o tipo de retorno (exceto para construtores de classe / estrutura, obviamente)
Como o método 2, o método 3 é compatível com a seleção de funções alternativas com a mesma assinatura.
fonte
foo()
função permanecerá disponível quando você o chamar com um segundo parâmetro explícito do modelo (afoo<double, double>();
chamada). E se permanecer disponível, há uma ambiguidade com a outra versão. Com o método 2, SFINAE habilita / desabilita o segundo argumento, não o parâmetro padrão. Portanto, você não pode chamá-lo explicando o parâmetro porque há uma falha de substituição que não permite um segundo parâmetro. Assim, a versão não estiver disponível, então não há ambigüidadeauto foo() -> std::enable_if_t<...>
geralmente é útil para evitar ocultar a assinatura da função e permitir o uso dos argumentos da função.Além da resposta do max66 , outro motivo para preferir o método 2 é que, com o método 1, você pode (acidentalmente) passar um parâmetro de tipo explícito como o segundo argumento do modelo e derrotar completamente o mecanismo SFINAE. Isso pode ocorrer como um erro de digitação, copiar / colar ou como uma supervisão em um mecanismo de modelo maior.
Demonstração ao vivo aqui
fonte