C ++ 17 apresenta variáveis embutidas
O C ++ 17 corrige esse problema para constexpr static
variáveis de membro que requerem uma definição fora de linha se ela foi usada com odr. Veja a segunda metade desta resposta para obter detalhes pré-C ++ 17.
A proposta P0386 Variáveis em linha apresenta a capacidade de aplicar o inline
especificador às variáveis. Em particular neste caso, constexpr
implica inline
em variáveis de membro estáticas. A proposta diz:
O especificador em linha pode ser aplicado a variáveis e também a funções. Uma variável declarada em linha tem a mesma semântica que uma função declarada em linha: ela pode ser definida de forma idêntica em várias unidades de tradução, deve ser definida em todas as unidades de tradução em que é usada pelo odr, e o comportamento do programa é como se existe exatamente uma variável.
e modificado [basic.def] p2:
Uma declaração é uma definição, a menos que
...
- declara um membro de dados estático fora de uma definição de classe e a variável foi definida dentro da classe com o especificador constexpr (esse uso foi descontinuado; consulte [depr.static_constexpr]),
...
e adicione [depr.static_constexpr] :
Para compatibilidade com as normas internacionais C ++ anteriores, um membro de dados estáticos constexpr pode ser redundantemente redeclarado fora da classe sem inicializador. Este uso está obsoleto. [Exemplo:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
- exemplo final]
C ++ 14 e versões anteriores
No C ++ 03, é permitido fornecer inicializadores em classe apenas para integrais const ou tipos de enumeração const ; no C ++ 11, constexpr
esse recurso foi estendido para tipos literais .
No C ++ 11, não precisamos fornecer uma definição de escopo de espaço para nome para um constexpr
membro estático, se não for usado por odr , podemos ver isso na seção padrão do C ++ 11 9.4.2
preliminar [class.static.data], que diz ( ênfase minha daqui para frente ):
[...] Um membro de dados estáticos do tipo literal pode ser declarado na definição de classe com o especificador constexpr; nesse caso, sua declaração deve especificar um colchete ou inicializador igual no qual toda cláusula de inicializador que é uma expressão de atribuição é uma expressão constante. [Nota: Nos dois casos, o membro pode aparecer em expressões constantes. - end note]
O membro ainda deve ser definido no escopo do espaço para nome, se for usado por odr (3.2) no programa e a definição do escopo do espaço para nome não deve conter um inicializador.
Então a pergunta se torna, é baz
usada aqui:
std::string str(baz);
e a resposta é sim ; portanto, também precisamos de uma definição de escopo de espaço para nome.
Então, como determinamos se uma variável é usada por odr ? O texto original do C ++ 11 na seção 3.2
[basic.def.odr] diz:
Uma expressão é potencialmente avaliada, a menos que seja um operando não avaliado (Cláusula 5) ou uma subexpressão dela. Uma variável cujo nome aparece como uma expressão potencialmente avaliada é usada como odr, a menos
que seja um objeto que satisfaça os requisitos para aparecer em uma expressão constante (5.19) e a conversão de valor em valor (4.1) seja aplicada imediatamente .
O baz
mesmo produz uma expressão constante, mas a conversão lvalue em rvalue não é aplicada imediatamente, pois não é aplicável por baz
ser uma matriz. Isso é abordado na seção 4.1
[conv.lval], que diz:
Um valor glvalue (3.10) de um tipo T sem função e sem matriz pode ser convertido em um valor preterido.53 [...]
O que é aplicado na conversão de matriz em ponteiro .
Essa redação de [basic.def.odr] foi alterada devido ao Relatório de Defeitos 712, pois alguns casos não foram cobertos por essa redação, mas essas alterações não alteram os resultados desse caso.