Compartilhar via


Atributos em C++

O Padrão C++ define um conjunto comum de atributos. Ele também permite que os fornecedores do compilador definam seus próprios atributos em um namespace específico do fornecedor. No entanto, os compiladores só são necessários para reconhecer os atributos definidos no padrão.

Em alguns casos, os atributos padrão se sobrepõem a parâmetros __declspec específicos do compilador. No Microsoft C++, você pode usar o atributo [[deprecated]] em vez de usar __declspec(deprecated). O atributo [[deprecated]] é reconhecido por qualquer compilador em conformidade. Para todos os outros parâmetros __declspec, como dllimport e dllexport, até agora não há nenhum atributo equivalente, portanto, você deve continuar a usar a sintaxe __declspec. Os atributos não afetam o sistema de tipos e não mudam o significado de um programa. Os compiladores ignoram valores de atributo que não reconhecem.

Visual Studio 2017 versão 15.3 e posterior (disponível com /std:c++17 e posterior): no escopo de uma lista de atributos, você pode especificar o namespace para todos os nomes com um único introdutor using:

void g() {
    [[using rpr: kernel, target(cpu,gpu)]] // equivalent to [[ rpr::kernel, rpr::target(cpu,gpu) ]]
    do task();
}

Atributos padrão do C++

No C++11, os atributos fornecem um modo padronizado de anotar constructos C++ (incluindo, mas não se limitando a classes, funções, variáveis e blocos) com informações adicionais. Os atributos podem ou não ser específicos do fornecedor. Um compilador pode usar essas informações para gerar mensagens informativas ou para aplicar lógica especial ao compilar o código atribuído. O compilador ignora todos os atributos que não reconhece, o que significa que você não pode definir seus próprios atributos personalizados usando essa sintaxe. Os atributos são colocados entre colchetes duplos:

[[deprecated]]
void Foo(int);

Os atributos representam uma alternativa padronizada às extensões específicas do fornecedor, como diretivas #pragma, __declspec() (Visual C++) ou __attribute__ (GNU). No entanto, você ainda precisará usar os constructos específicos do fornecedor para a maioria das finalidades. Atualmente, o padrão especifica os seguintes atributos que um compilador em conformidade deve reconhecer.

[[carries_dependency]]

O atributo [[carries_dependency]] especifica que a função propaga a ordenação de dependência de dados para sincronização de threads. O atributo pode ser aplicado a um ou mais parâmetros, para especificar que o argumento passado carrega uma dependência no corpo da função. O atributo pode ser aplicado à função para especificar que o valor retornado carrega uma dependência fora da função. O compilador pode usar essas informações para gerar um código mais eficiente.

[[deprecated]]

Visual Studio 2015 e posterior: O atributo [[deprecated]] especifica que uma função não se destina ao uso. Ou, que talvez não exista em versões futuras de uma interface de biblioteca. O atributo [[deprecated]] pode ser aplicado à declaração de uma classe, um nome de typedef, uma variável, um membro de dados não estático, uma função, um namespace, uma enumeração, um enumerador ou uma especialização de modelo. O compilador pode usar esse atributo para gerar uma mensagem informativa quando o código do cliente tenta chamar a função. Quando o compilador do Microsoft C++ detecta o uso de um [[deprecated]] item, ele gera o aviso do compilador C4996.

[[fallthrough]]

Visual Studio 2017 e posterior: (Disponível com /std:c++17 e posterior.) O atributo [[fallthrough]] pode ser usado no contexto de instruções switch como uma dica para o compilador (ou qualquer pessoa que esteja lendo o código) de que o comportamento de fallthrough se destina. O compilador do Microsoft C++ atualmente não avisa sobre o comportamento de fallthrough, portanto, esse atributo não tem efeito no comportamento do compilador.

[[likely]]

Visual Studio 2019 versão 16.6 e posterior: (Disponível com /std:c++20 e posterior.) O atributo [[likely]] especifica uma dica para o compilador de que o caminho de código para o rótulo ou instrução atribuído tem maior probabilidade de ser executado do que as alternativas. No compilador da Microsoft, o atributo [[likely]] marca blocos como "código frequente", o que incrementa uma pontuação de otimização interna. A pontuação é incrementada mais ao otimizar quanto a velocidade e não tanto ao otimizar em relação ao tamanho. A pontuação líquida afeta a probabilidade de sublinhar, cancelar o registro de loop e vetorizar otimizações. O efeito de [[likely]] e [[unlikely]] é semelhante à otimização guiada por perfil, mas limitado no escopo da unidade de tradução atual. A otimização de reordenação de bloco ainda não foi implementada para esse atributo.

[[maybe_unused]]

Visual Studio 2017 versão 15.3 e posterior: (Disponível com /std:c++17 e posterior.) O atributo [[maybe_unused]] especifica que uma variável, função, classe, typedef, membro de dados não estático, enumeração ou especialização de modelo pode ser intencionalmente não utilizada. O compilador não avisa quando uma entidade marcada como [[maybe_unused]] não é usada. Uma entidade declarada sem o atributo pode ser reenviada posteriormente com o atributo e vice-versa. Uma entidade é considerada marcada após sua primeira declaração marcada como [[maybe_unused]] ser analisada e para o restante da unidade de tradução atual.

[[nodiscard]]

Visual Studio 2017 versão 15.3 e posterior: (Disponível com /std:c++17 e posterior.) Especifica que o valor retornado de uma função não se destina a ser descartado. Gera o aviso C4834, conforme mostrado neste exemplo:

[[nodiscard]]
int foo(int i) { return i * i; }

int main()
{
    foo(42); //warning C4834: discarding return value of function with 'nodiscard' attribute
    return 0;
}

[[noreturn]]

O atributo [[noreturn]] especifica que uma função nunca retorna, em outras palavras, ela sempre lança uma exceção ou sai. O compilador pode ajustar suas regras de compilação para entidades [[noreturn]].

[[unlikely]]

Visual Studio 2019 versão 16.6 e posterior: (Disponível com /std:c++20 e posterior.) O atributo [[unlikely]] especifica uma dica para o compilador de que o caminho de código para o rótulo ou instrução atribuído tem maior probabilidade de ser executado do que as alternativas. No compilador da Microsoft, o atributo [[unlikely]] marca blocos como "código frio", o que subtrai uma pontuação de otimização interna. A pontuação é subtraída mais ao otimizar quanto ao tamanho e não tanto ao otimizar em relação à velocidade. A pontuação líquida afeta a probabilidade de sublinhar, cancelar o registro de loop e vetorizar otimizações. A otimização de reordenação de bloco ainda não foi implementada para esse atributo.

Atributos específicos da Microsoft

[[gsl::suppress(<tag> [, justification: <narrow-string-literal>])]]

<tag> é uma cadeia de caracteres que especifica o nome da regra a ser suprimida. O campo opcional justification permite explicar por que um aviso está sendo desabilitado ou suprimido. Esse valor aparecerá na saída SARIF quando a /analyze:log:includesuppressed opção for especificada. Seu valor é um literal de cadeia de caracteres estreita codificado em UTF-8. O [[gsl::suppress]] atributo está disponível no Visual Studio 2022 versão 17.14 e versões posteriores.

O atributo específico da Microsoft [[gsl::suppress]] é usado para suprimir avisos de verificadores que impõem regras da GSL (Biblioteca de Suporte de Diretrizes) no código. Por exemplo, considere este snippet de código:

int main()
{
    int arr[10]; // GSL warning C26494 will be fired
    int* p = arr; // GSL warning C26485 will be fired
    [[gsl::suppress("bounds.1", justification: "This attribute suppresses Bounds rule #1")]]
    {
        int* q = p + 1; // GSL warning C26481 suppressed
        p = q--; // GSL warning C26481 suppressed
    }
}

O exemplo gera estes avisos:

  • C26494 (Regra do Tipo 5: inicializar sempre um objeto.)

  • C26485 (Regra de limites 3: nenhuma matriz para decaimento de ponteiro.)

  • C26481 (Regra de limites 1: não usar aritmética de ponteiro. Usar período em vez disso.)

Os dois primeiros avisos são disparados quando você compila esse código com a ferramenta de análise de código CppCoreCheck instalada e ativada. Mas o terceiro aviso não é acionado por causa do atributo. Você pode suprimir todo o perfil de limites escrevendo [[gsl::suppress("bounds")]] sem incluir um número de regra específico. As Diretrizes Principais do C++ foram criadas para ajudar você a escrever um código melhor e mais seguro. O atributo de supressão facilita a desligar os avisos quando eles não são desejados.

[[msvc::flatten]]

O atributo específico da Microsoft [[msvc::flatten]] é muito semelhante ao [[msvc::forceinline_calls]], e pode ser usado nos mesmos locais e da mesma maneira. A diferença é que [[msvc::flatten]] irá [[msvc::forceinline_calls]] todas as chamadas no escopo ao qual é aplicado de forma recursiva, até que não haja mais chamadas. Isso pode ter consequências para o crescimento do tamanho do código resultante da função ou a taxa de transferência do compilador, que você deve gerenciar manualmente.

[[msvc::forceinline]]

Quando colocado antes de uma declaração de função, o atributo específico da Microsoft [[msvc::forceinline]] tem o mesmo significado que __forceinline.

[[msvc::forceinline_calls]]

O atributo específico da Microsoft [[msvc::forceinline_calls]] pode ser colocado em ou antes de uma instrução ou de um bloco. Isso faz com que a heurística embutida tente todas as chamadas [[msvc::forceinline]] nessa instrução ou bloco:

void f() {
    [[msvc::forceinline_calls]]
    {
        foo();
        bar();
    }
    ...
    [[msvc::forceinline_calls]]
    bar();
    
    foo();
}

A primeira chamada para foo, e ambas as chamadas para bar, são tratadas como se tivessem sido declaradas __forceinline. A segunda chamada para foo não é tratada como __forceinline.

[[msvc::intrinsic]]

O atributo [[msvc::intrinsic]] tem três restrições na função à qual é aplicado:

  • A função não pode ser recursiva. Seu corpo deve ter apenas uma instrução return com um static_cast do tipo de parâmetro para o tipo de retorno.
  • A função só pode aceitar um único parâmetro.
  • A opção do compilador /permissive- é necessária. (As opções /std:c++20 e posteriores implicam /permissive- por padrão.)

O atributo específico da Microsoft [[msvc::intrinsic]] informa ao compilador para embutir uma metafunção que atua como uma conversão nomeada do tipo de parâmetro para o tipo de retorno. Quando o atributo está presente em uma definição de função, o compilador substitui todas as chamadas para essa função por uma conversão simples. O atributo [[msvc::intrinsic]] está disponível no Visual Studio 2022 versão 17.5 versão prévia 2 e versões posteriores. Esse atributo se aplica somente à função específica que o segue.

Exemplo

Neste código de exemplo, o atributo [[msvc::intrinsic]] aplicado à função my_move faz com que o compilador substitua chamadas para a função pela conversão estática embutida em seu corpo:

template <typename T>
[[msvc::intrinsic]] T&& my_move(T&& t) { return static_cast<T&&>(t); }

void f() {
    int i = 0;
    i = my_move(i);
}

[[msvc::noinline]]

Quando colocado antes de uma declaração de função, o atributo específico da Microsoft [[msvc::noinline]] tem o mesmo significado que __declspec(noinline).

[[msvc::noinline_calls]]

O atributo específico da Microsoft [[msvc::noinline_calls]] tem o mesmo uso que [[msvc::forceinline_calls]]. Ele pode ser colocado antes de qualquer instrução ou bloco. Em vez de forçar o inlining de todas as chamadas nesse bloco, ele tem o efeito de desativar o inlining para o escopo ao qual é aplicado.

[[msvc::no_tls_guard]]

O atributo específico da Microsoft [[msvc::no_tls_guard]] desabilita as verificações de inicialização no primeiro acesso para variáveis locais de thread em DLLs. As verificações são habilitadas por padrão no código criado usando o Visual Studio 2019 versão 16.5 e versões posteriores. Esse atributo se aplica somente para a variável específica que o segue. Para desabilitar verificações globalmente, use a opção do compilador /Zc:tlsGuards-.