Eu quero ter uma static const
char
matriz na minha classe. O GCC reclamou e me disse que eu deveria usar constexpr
, embora agora esteja me dizendo que é uma referência indefinida. Se eu fizer da matriz um não membro, ela será compilada. O que está acontecendo?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
c++
c++11
static-members
constexpr
Pubby
fonte
fonte
int
@MooingDuck Funciona bem como um não membro. Isso não violaria a regra também?int
fraude s. Como um terceiro, que não deve ser permitido, a menos que as regras mudado para C ++ 11 (possível)Respostas:
Adicione ao seu arquivo cpp:
Razão: Você deve fornecer a definição do membro estático, bem como a declaração. A declaração e o inicializador vão para dentro da definição de classe, mas a definição de membro deve ser separada.
fonte
decltype(foo::baz) constexpr foo::baz;
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
implicainline
em variáveis de membro estáticas. A proposta diz:e modificado [basic.def] p2:
e adicione [depr.static_constexpr] :
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 ++ 119.4.2
preliminar [class.static.data], que diz ( ênfase minha daqui para frente ):Então a pergunta se torna, é
baz
usada aqui: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:O
baz
mesmo produz uma expressão constante, mas a conversão lvalue em rvalue não é aplicada imediatamente, pois não é aplicável porbaz
ser uma matriz. Isso é abordado na seção4.1
[conv.lval], que diz: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.
fonte
constexpr
tem absolutamente nada a ver com isso? (baz
É uma expressão de qualquer maneira constante)integral or enumeration type
mas caso contrário, sim, o que importa é que seja uma expressão constante .Isso é realmente uma falha no C ++ 11 - como outros explicaram, no C ++ 11 uma variável de membro constexpr estática, diferente de qualquer outro tipo de variável global constexpr, possui ligação externa, portanto deve ser explicitamente definida em algum lugar.
Também vale a pena notar que, na prática, você pode, na prática, se livrar de variáveis de membro constexpr estáticas sem definições ao compilar com otimização, pois elas podem acabar embutidas em todos os usos, mas se você compilar sem otimização, muitas vezes seu programa falhará ao vincular. Isso torna uma armadilha oculta muito comum - seu programa é compilado com otimização, mas assim que você desativa a otimização (talvez para depuração), ele não consegue se conectar.
Boas notícias - essa falha foi corrigida no C ++ 17! Entretanto, a abordagem é um pouco complicada: no C ++ 17, as variáveis de membro constexpr estáticas estão implicitamente alinhadas . A aplicação embutida nas variáveis é um novo conceito no C ++ 17, mas significa efetivamente que elas não precisam de uma definição explícita em lugar algum.
fonte
A solução mais elegante não é mudar
char[]
para:Dessa forma, podemos ter a definição / declaração / inicializador em 1 linha de código.
fonte
char[]
você pode usarsizeof
para obter o comprimento da string em tempo de compilação, comchar *
você não pode (ele retornará a largura do tipo de ponteiro, 1 neste caso).sizeof
problema, e pode ser usado em "header-apenas" soluçõesMinha solução alternativa para o vínculo externo de membros estáticos é usar
constexpr
getters de membros de referência (que não se deparam com o problema @gnzlbg levantado como um comentário à resposta de @deddebme).Esse idioma é importante para mim porque detesto ter vários arquivos .cpp em meus projetos e tento limitar o número a um, que consiste em nada além de se
#include
umamain()
função.fonte
No meu ambiente, a versão do gcc é 5.4.0. Adicionar "-O2" pode corrigir esse erro de compilação. Parece que o gcc pode lidar com esse caso ao solicitar otimização.
fonte