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 alteram 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. O padrão atualmente especifica os seguintes atributos que um compilador em conformidade deve reconhecer.

[[carries_dependency]]

O [[carries_dependency]] atributo especifica que a função propaga a ordem 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 [[deprecated]] atributo 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 [[deprecated]] atributo pode ser aplicado à declaração de uma classe, um typedef-name, uma variável, um membro de dados não estáticos, 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 [[likely]] atributo especifica uma dica para o compilador de que o caminho de código para o rótulo ou instrução atribuída tem mais probabilidade de ser executado do que 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 blocos 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 [[maybe_unused]] atributo especifica que uma variável, função, classe, typedef, membro de dados não estáticos, enum 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 [[noreturn]] atributo especifica que uma função nunca retorna, ou seja, 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 [[unlikely]] atributo especifica uma dica para o compilador de que o caminho de código para o rótulo ou instrução atribuído tem menos 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 blocos ainda não foi implementada para esse atributo.

Atributos específicos da Microsoft

[[gsl::suppress(rules)]]

O atributo específico [[gsl::suppress(rules)]] da Microsoft é usado para suprimir avisos de verificadores que impõem regras de GSL (Guidelines Support Library) 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)]] // 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 de tipo 5: sempre inicializar um objeto.)

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

  • C26481 (Regra de limites 1: não use aritmética de ponteiro. Em vez disso, use o intervalo.)

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 [[msvc::flatten]] específico da Microsoft é muito semelhante ao , e pode ser usado nos mesmos lugares e da [[msvc::forceinline_calls]]mesma maneira. A diferença é que todas as chamadas no escopo ao qual é aplicado recursivamente, até que [[msvc::flatten]][[msvc::forceinline_calls]] nenhuma chamada seja deixada. 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 [[msvc::forceinline]] específico da Microsoft tem o mesmo significado que __forceinline.

[[msvc::forceinline_calls]]

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

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

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

[[msvc::intrinsic]]

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

  • A função não pode ser recursiva; seu corpo só deve ter uma instrução return com a 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 /std:c++20 opções e posteriores implicam /permissive- por padrão.)

O atributo específico [[msvc::intrinsic]] da Microsoft diz 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 [[msvc::intrinsic]] atributo está disponível no Visual Studio 2022 versão 17.5 preview 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 aplicado à função faz com que my_move o [[msvc::intrinsic]] compilador substitua as chamadas para a função com a 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 [[msvc::noinline]] específico da Microsoft tem o mesmo significado que declspec(noinline).

[[msvc::noinline_calls]]

O atributo [[msvc::noinline_calls]] específico da Microsoft tem o mesmo uso que [[msvc::forceinline_calls]]o . Ele pode ser colocado antes de qualquer instrução ou bloqueio. Em vez de forçar a entrada 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 [[msvc::no_tls_guard]] da Microsoft desabilita as verificações de inicialização no primeiro acesso a variáveis de thread local 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 à variável específica que o segue. Para desabilitar verificações globalmente, use a opção do /Zc:tlsGuards- compilador.