Última atualização:2026-10-12
Dezessete funcionalidades avançadas do C++17¶
Panorama completo de 17 funcionalidades avançadas do C++17¶
O C++17 é uma das versões mais usadas atualmente; hoje reservei um tempo para organizar 17 recursos importantes — e os recursos existentes não se resumem a esses.
1. Algoritmos paralelos¶
O C++17 introduziu versões paralelas de muitos algoritmos da biblioteca padrão. Esses algoritmos podem ser executados em paralelo e, portanto, podem trazer ganhos significativos de desempenho em sistemas multinúcleo.
Já escrevi antes um artigo apresentando esse recurso de forma completa; você pode ler neste link:
Exemplo:
#include <algorithm>
#include <vector>
#include <execution>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
std::sort(std::execution::par, v.begin(), v.end());
}
Neste exemplo, std::sort é executado em paralelo, ordenando os elementos do vetor v de forma paralela.
2. Inicializadores de if¶
O inicializador de if do C++17 é um recurso que permite inicializar variáveis diretamente na instrução if. Essa forma de inicialização pode, até certo ponto, melhorar a legibilidade e a concisão do código.
No C++ tradicional, normalmente inicializaríamos variáveis assim:
Já no C++17, podemos usar o inicializador de if para simplificar esse processo, tornando o código mais compacto:
Neste exemplo, colocamos a inicialização da variável x diretamente na instrução if. A variável condition é definida e inicializada na instrução if e fica disponível no bloco do if. Essa forma é mais intuitiva e concisa, especialmente em inicializações condicionais simples.
3. Dedução de argumentos de template de classe (CTAD)¶
O CTAD permite que o compilador deduza automaticamente os argumentos de template a partir dos argumentos de função. Isso facilita o uso de templates sem a necessidade de especificar explicitamente os argumentos de template.
Artigo anterior com uma apresentação completa deste recurso:
Exemplo:
Neste exemplo, ao chamar foo(42), o compilador deduz que o tipo de T é int.
4. template <auto>¶
A palavra-chave de template foi introduzida como espaço reservado para parâmetros de template não tipo. Ela permite representar valores de qualquer tipo em um template.
Exemplo:
template<auto value>
struct constant {
static constexpr auto get_value() { return value; }
};
// 用法
static_assert(constant<42>::get_value() == 42);
5. std::optional e std::variant¶
std::optional e std::variant são dois novos tipos introduzidos no C++17. std::optional representa um valor que pode ou não existir; std::variant representa uma união type-safe, capaz de armazenar valores de tipos diferentes.
Exemplo:
#include <optional>
#include <variant>
int main() {
std::optional<int> opt = 42;
std::variant<int, double> var = 3.14;
}
Neste exemplo, opt é um inteiro opcional contendo o valor 42, e var é um variant contendo o valor 3.14.
6. Expressões de dobra (fold expressions)¶
No C++17, as expressões de dobra fornecem uma forma concisa de aplicar operações binárias a um pacote de parâmetros. Elas permitem executar operações como somar, multiplicar ou concatenar elementos de um pacote de parâmetros sem recursão ou iteração explícita.
Por exemplo:
#include <iostream>
template<typename T>
T sum(T t) {
return t;
}
// 使用折叠表达式的递归情况
template<typename T, typename... Args>
T sum(T first, Args... args) {
return first + sum(args...);
}
int main() {
int total = sum(1, 2, 3, 4, 5);
std::cout << "总和: " << total << std::endl;
return 0;
}
A expressão de dobra (first + ... + args) na função sum recursiva aplica a operação de adição a cada elemento do pacote de parâmetros.
7. Vinculações estruturadas (structured bindings)¶
As vinculações estruturadas permitem decompor um objeto em seus elementos constituintes, de modo semelhante ao desempacotamento de tuplas que você talvez conheça.
Artigo anterior:
Exemplo:
#include <tuple>
#include <string>
int main() {
std::tuple<int, std::string, double> t(42, "hello", 3.14);
auto [i, s, d] = t; // i = 42, s = "hello", d = 3.14
}
Neste exemplo, a vinculação estruturada [i, s, d] decompõe a tupla t em seus elementos constituintes.
8. Parâmetros de template de template¶
Por exemplo: no C++17, a sintaxe template<template<class...>typename bob> struct foo {} declara um template chamado foo que aceita um parâmetro de template de template chamado bob. O parâmetro de template de template bob, por sua vez, aceita qualquer quantidade de parâmetros de tipo de template.
template <template<class...> typename bob>
struct foo {
template<typename T>
void bar(const bob<T>& arg) {
std::cout << "size: " << arg.size() << std::endl;
}
};
int main() {
foo<std::vector> f;
std::vector<int> vec = {1, 2, 3, 4, 5};
f.bar(vec);
return 0;
}
Na função main, instanciamos foo usando std::vector como argumento de template para bob. Isso nos permite criar uma estrutura genérica foo que funciona com qualquer template que aceite qualquer quantidade de parâmetros de tipo, como std::vector, std::list ou templates definidos pelo usuário.
9. Variáveis inline¶
O C++17 permite definir variáveis como inline dentro da definição da classe, o que pode ajudar a reduzir o tamanho do binário e possivelmente melhorar o desempenho ao evitar cópias duplicadas da variável em múltiplas unidades de tradução.
Exemplo:
class MyClass {
public:
inline static int inlineVar = 42;
};
int main() {
int localVar = MyClass::inlineVar;
}
Aqui, inlineVar é uma variável-membro estática inline de MyClass.
10. Melhorias em atributos¶
O C++17 oferece novos atributos e aprimora os já existentes, permitindo que os desenvolvedores forneçam ao compilador mais informações sobre o comportamento do código.
Exemplo:
[[nodiscard]] int get_sum(int a, int b) {
return a + b;
}
int main() {
auto result = get_sum(1, 2);
// 编译器可能会警告‘result’未使用
}
Neste exemplo, [[nodiscard]] é um atributo que pode ser aplicado a funções, indicando que o valor de retorno não deve ser descartado pelo chamador.
11. Namespaces aninhados¶
O C++17 aprimorou os templates variádicos por meio das expressões de dobra, tornando o código mais conciso e expressivo ao lidar com pacotes de parâmetros.
Exemplo:
// 外部命名空间
namespace outer {
// 内部命名空间
namespace inner {
void foo() {
std::cout << "在内部命名空间中" << std::endl;
}
}
}
outer::inner::foo();
A definição de namespaces aninhados fornece uma forma de organizar o código em camadas, melhorando a legibilidade e a manutenibilidade, especialmente em projetos grandes. Elas também ajudam a evitar conflitos de nomes ao fornecer uma hierarquia de namespaces mais estruturada.
12. Melhorias em literais¶
O C++17 aprimorou os literais, incluindo melhorias nos literais inteiros e de ponto flutuante, além do suporte aos literais true e false.
Exemplo:
auto num = 123_456; // Underscore in integer literals
auto pi = 3.1415_f; // Suffix for floating-point literals
13. constexpr Lambda¶
O C++17 permite que funções lambda sejam constexpr, se atenderem às condições, podendo ser usadas em contextos que exigem avaliação em tempo de compilação, por exemplo:
Neste exemplo, lambda é uma lambda constexpr que recebe um inteiro x como parâmetro e retorna o dobro de x. O static_assert verifica, em tempo de compilação, se o valor de lambda(5) é igual a 10.
14. Captura de *this¶
Em lambdas, capturar *this tornou-se mais simples, permitindo o acesso direto aos membros do objeto contido.
class Foo {
int data = 42;
public:
auto member_lambda() {
return [*this] { std::cout << data << std::endl; };
}
};
// 使用
Foo f;
f.member_lambda()(); // 输出 "42"
15. Instruções if e switch estendidas¶
A condição de uma instrução if ou switch agora pode ser qualquer expressão, não se limitando a condições booleanas. Isso torna o fluxo de controle mais flexível, por exemplo ao usar vinculações estruturadas:
Neste exemplo, a instrução if verifica se a variável inserted é verdadeira, mas a condição inclui também a atribuição da vinculação estruturada.
16. Loop for baseado em intervalo generalizado¶
Essa melhoria oferece suporte a sentinela ou iterador final de tipo diferente do iterador inicial, o que ajuda no tratamento de coleções terminadas em nulo e em casos semelhantes. Por exemplo:
Neste exemplo, my_container pode ser um contêiner que usa um tipo diferente de iterador final, mas o loop ainda funciona corretamente.
17. if constexpr¶
Esse recurso permite que o compilador avalie a condição em tempo de compilação, possibilitando código mais genérico. Se a condição for verdadeira, o código dentro do bloco if é incluído no código compilado; caso contrário, é descartado. Isso pode simplificar o código ao eliminar ramificações desnecessárias em tempo de execução. Por exemplo:
Neste exemplo, a instrução if constexpr verifica se o tipo T é int e inclui o código apropriado em conformidade.
Resumo¶
Por ora é isso; os demais conteúdos serão abordados na próxima seção.
Tópicos relacionados¶
- Atributo [[nodiscard]] no C++17 | C++那些事
- C++17 std::string_view | C++那些事
- Recursos de memória PMR no C++17 | C++那些事
- Dedução de argumentos de template de classe CTAD no C++17 | C++那些事
- C++17 optional_ref | C++那些事
- C++17 std::variant | C++那些事
- Vinculações estruturadas no C++17 | C++那些事
- Dedução com auto em templates no C++17 | C++那些事
- Templates de variável no C++17 | C++那些事