0Pricing
C++ Academy · Aula

SFINAE

Habilite funções condicionalmente

SFINAE é uma aula grátis de C++ Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C++ Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C++ Academy inclui 4 aulas no total.

O que significa SFINAE?

SFINAE significa "Falha de Substituição Não é um Erro". Quando o compilador substitui os argumentos do modelo e o resultado é malformado, esse candidato é simplesmente removido, em vez de causar um erro grave.

  • Ela permite sobrecargas condicionais.
  • É a base da seleção em tempo de compilação.
#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
isOdd(T n) { return n % 2 != 0; }

int main() {
    std::cout << std::boolalpha << isOdd(3) << '\n';
    return 0;
}

Falha de Substituição na Prática

Se substituir um tipo tornar inválida a assinatura de uma função, o compilador a ignora silenciosamente e continua procurando outros candidatos.

#include <iostream>

template <typename T>
auto sizeOfValue(T t) -> decltype(t.size()) { return t.size(); }

int main() {
    std::string s = "hello";
    std::cout << sizeOfValue(s) << '\n';
    return 0;
}

Duas Sobrecargas, Uma Vence

Forneça duas sobrecargas nas quais exatamente uma seja substituída com sucesso para um determinado tipo. SFINAE descarta a inválida.

#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_integral<T>::value, const char*>::type
name() { return "integral"; }

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, const char*>::type
name() { return "floating"; }

int main() {
    std::cout << name<int>() << ' ' << name<double>() << '\n';
    return 0;
}

decltype e SFINAE de Expressão

Usar decltype em uma expressão do tipo de retorno faz com que uma função exista apenas se essa expressão for válida para o tipo.

#include <iostream>

template <typename T>
auto tryDouble(T x) -> decltype(x + x) { return x + x; }

int main() {
    std::cout << tryDouble(21) << '\n';
    std::cout << tryDouble(2.5) << '\n';
    return 0;
}

Detectando um Membro

Um truque clássico de SFINAE detecta se um tipo tem determinado membro verificando se uma expressão que o utiliza pode ser compilada.

#include <iostream>
#include <type_traits>
#include <vector>

template <typename T>
auto hasSize(int) -> decltype(std::declval<T>().size(), std::true_type{});

template <typename T>
std::false_type hasSize(...);

int main() {
    std::cout << std::boolalpha;
    std::cout << decltype(hasSize<std::vector<int>>(0))::value << '\n';
    std::cout << decltype(hasSize<int>(0))::value << '\n';
    return 0;
}

A Alternativa de Reserva ...

Uma sobrecarga que recebe ... (argumentos variáveis) tem a menor prioridade. Ela funciona como opção abrangente quando a sobrecarga restringida por SFINAE falha.

#include <iostream>

template <typename T>
auto pick(int) -> decltype(T{}.value, void()) { std::cout << "has value\n"; }

template <typename T>
void pick(...) { std::cout << "no value\n"; }

struct WithValue { int value = 0; };

int main() {
    pick<WithValue>(0);
    pick<int>(0);
    return 0;
}

void_t

No C++17, std::void_t simplifica a detecção: ele mapeia quaisquer tipos bem formados para void, de modo que uma especialização seja ativada apenas quando a expressão verificada for válida.

#include <iostream>
#include <type_traits>
#include <vector>

template <typename T, typename = void>
struct HasSize : std::false_type {};

template <typename T>
struct HasSize<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};

int main() {
    std::cout << std::boolalpha;
    std::cout << HasSize<std::vector<int>>::value << '\n';
    std::cout << HasSize<int>::value << '\n';
    return 0;
}

Por que não Apenas um Erro?

Sem SFINAE, uma substituição inválida seria um erro grave de compilação, do qual não seria possível se recuperar. SFINAE a transforma silenciosamente em "não é um candidato".

#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_pointer<T>::value, bool>::type
isNull(T p) { return p == nullptr; }

int main() {
    int x = 0;
    std::cout << std::boolalpha << isNull(&x) << '\n';
    return 0;
}

Alternativa com Despacho por Marcadores

SFINAE pode ser difícil de ler. O despacho por marcadores escolhe uma implementação usando pequenos tipos marcadores e a resolução de sobrecarga.

#include <iostream>
#include <type_traits>

template <typename T>
void impl(T v, std::true_type) { std::cout << "integral: " << v << '\n'; }

template <typename T>
void impl(T v, std::false_type) { std::cout << "other: " << v << '\n'; }

template <typename T>
void handle(T v) { impl(v, std::is_integral<T>{}); }

int main() {
    handle(5);
    handle(2.5);
    return 0;
}

SFINAE e if constexpr

No C++17, if constexpr frequentemente substitui SFINAE para ramificações dentro de uma única função, pois os ramos não utilizados são descartados em tempo de compilação.

#include <iostream>
#include <type_traits>

template <typename T>
void describe(T v) {
    if constexpr (std::is_integral<T>::value) {
        std::cout << "integral: " << v << '\n';
    } else {
        std::cout << "other: " << v << '\n';
    }
}

int main() {
    describe(7);
    describe(3.14);
    return 0;
}

Juntando Tudo

SFINAE é especialmente útil quando você precisa selecionar entre modelos de função separados com base nas propriedades do tipo, no nível da sobrecarga.

#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_signed<T>::value, T>::type
absValue(T v) { return v < 0 ? -v : v; }

int main() {
    std::cout << absValue(-9) << '\n';
    return 0;
}

Verificação Rápida

Teste sua compreensão de SFINAE.

Recapitulação

Você aprendeu sobre SFINAE:

  • uma substituição inválida remove um candidato em vez de gerar um erro
  • é usada com enable_if, decltype e void_t para habilitar funções condicionalmente
  • uma sobrecarga com ... funciona como alternativa de reserva
  • as alternativas modernas incluem despacho por marcadores e if constexpr

Em seguida, você se concentrará detalhadamente nos padrões clássicos de enable_if.

Perguntas Frequentes

A aula “SFINAE” é grátis?

Sim — o texto completo de “SFINAE” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C++ Academy, atualize para CoddyKit PRO. O curso de C++ Academy inclui 4 aulas no total.

O que vou aprender em “SFINAE”?

Habilite funções condicionalmente Você pratica C++ Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C++ Academy?

Nenhuma experiência prévia é necessária. C++ Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “SFINAE”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C++ Academy?

Sim. Cada aula de C++ Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Especialização completa
  2. Especialização parcial
  3. SFINAE
  4. Padrões de enable_if
← Voltar para C++ Academy