Nas funções constexpr do C ++ 11, uma segunda instrução como uma assert()
não é possível. A static_assert()
é bom, mas não funcionaria se a função fosse chamada como função 'normal'. O operador de vírgula pode vir para ajudar. o assert()
, mas é feio e algumas ferramentas cospem avisos sobre ele.
Considere esse 'getter' que é perfeitamente possível ao lado da afirmação. Mas eu gostaria de manter algum tipo de asserção para tempo de execução e tempo de compilação, mas não posso simplesmente sobrecarregar, dependendo do contexto 'constexpr'.
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
ASSERT( idx < Size ); // a no-go for constexpr funcs in c++11
// not possible, even in constexpr calls as being pointed out, but what I would like:
static_assert( idx < Size, "out-of-bounds" );
return m_vals[idx];
}
};
Condições secundárias: C ++ 11, sem heap, sem exceções, sem detalhes específicos do compilador.
Observe como os comentaristas apontaram (obrigado!), static_assert
O argumento não é possível (mas seria bom). O compilador me deu um erro diferente no acesso fora dos limites nessa situação.
static_assert
dependenteidx
em tudo. Você só pode diagnosticar um valor incorretoidx
se a função for usada em um contexto que exija uma expressão constante, forçando a avaliação de uma construção que a torna não uma expressão constante. Fora desse contexto, você nunca pode verificar o valor em tempo de compilação.Respostas:
Algo como
Ele fornecerá um erro de tempo de compilação na falha de asserção, se usado em um contexto que exija uma expressão constante (porque chamará uma não-
constexpr
função).Caso contrário, ele falhará em tempo de execução com uma chamada para
assert
(ou seu analógico).É o melhor que você pode fazer, até onde eu sei. Não há como usar o valor de
idx
para forçar uma verificação em tempo de compilação fora do contexto, exigindo expressões constantes.A sintaxe do operador de vírgula não é boa, mas as
constexpr
funções do C ++ 11 são muito limitadas.Obviamente, como você já observou, o comportamento indefinido será diagnosticado de qualquer maneira se a função for usada em um contexto que exija uma expressão constante.
Se você sabe que
assert
(ou o seu análogo) não se expande para algo proibido em uma expressão constante, se a condição é avaliada,true
mas o faz se for avaliadofalse
, você pode usá-lo diretamente em vez demy_assert
pular a indireta que eu construo no meu códigofonte
(void)0
noNDEBUG
caso evoid()
no outro? Ou é realmente o mesmo?(void)0
é um no-op, ele não compila nada (que é o que você deseja quandoNDEBUG
é definido), enquanto você precisa dovoid()
para que o segundo e o terceiro operandos do operador condicional tenham o mesmo tipovoid
,.(void)0
que seria bom em todos os casos também. Acabei de substituí-lo no primeiro caso, porquevoid()
também pode ser analisado como tipo de função sem parâmetros e sem tipo de retorno, dependendo do contexto. Não pode ser analisado dessa maneira nas subexpressões no segundo caso.Melhor que uma expressão de vírgula, você pode usar uma condicional ternária. O primeiro operando é o seu predicado de asserção, o segundo operando é a sua expressão de sucesso e, como o terceiro operando pode ser qualquer expressão - mesmo que não seja utilizável em um contexto constante de C ++ 11 - você pode usar um lambda para chamar as
ASSERT
instalações da sua biblioteca :Explicação do corpo da lambda:
ASSERT(false && (pred))
é garantir que seu mecanismo de asserção seja chamado com uma expressão apropriada (para stringification).struct nxg { nxg() {} } nxg
é para segurança futura, para garantir que, se você compilar em C ++ 17 ou superior comNDEBUG
o lambda ainda não forconstexpr
, a asserção será imposta no contexto de avaliação const.return (success)
existe por dois motivos: garantir que o segundo e o terceiro operandos tenham o mesmo tipo e, se a sua biblioteca respeitar,NDEBUG
asuccess
expressão será retornada independentementepred
. (pred
serão avaliados , mas você espera que os predicados de afirmação sejam baratos para avaliar e não tenham efeitos colaterais).Exemplo de uso:
fonte
[&]
/[&] -> decltype((success))
para conservar referências.pred
avaliação deve ser barata, mas nem sempre. Então, como umaASSERT_EXPR
macro geral, eu não recomendaria. Às vezes, faço chamadas caras e me afirmo (por exemplo, para verificar invariantes).NDEBUG
a desativação de declarações de tempo de execução, ainda deseja que as declarações em tempo de compilação sejam verificadas. Tornar o corpo do lambda de caso de falha nãoconstexpr
é uma maneira de garantir isso - mas tem o custo de avaliar e descartar o predicado no tempo de execuçãoNDEBUG
. Caso contrário, você pode definir a macro emNDEBUG
apenasreturn (success);
.static_assert
não pode ser usado aqui. O argumento para umaconstexpr
função não é permitido em uma expressão constante. Portanto, não há solução para o seu problema sob as restrições fornecidas.No entanto, podemos resolver o problema dobrando duas restrições
não usar
static_assert
(use outros métodos para produzir um diagnóstico em tempo de compilação) edesconsidere que o operador de vírgula "é feio e algumas ferramentas emitem avisos sobre isso". (Mostrar sua feiúra é uma conseqüência infeliz dos requisitos estritos das
constexpr
funções do C ++ 11 )Então, podemos usar um normal
assert
:Em um contexto de avaliação constante, isso emitirá um erro do compilador
error: call to non-'constexpr' function 'void __assert_fail(const char*, const char*, unsigned int, const char*)'
.fonte
Isto que funciona para mim para a maioria dos compiladores: https://godbolt.org/z/4nT2ub
Agora
static_assert
está obsoleto, poisconstexpr
não pode conter comportamento indefinido. Portanto, quando você interrompe o compilador de índice de matriz, reporta o erro adequado. Veja aqui .O problema é
assert
. É uma macro cuja implementação é indefinida. Se o compilador usar uma função que não é uma,constexpr
ela falhará, mas como você pode ver, três compiladores principais não terão problemas com isso.fonte
C++11
isso falhar somente no gcc godbolt.org/z/DB2zL3/std:c++11
sinalizador e Clang permite o código, embora exija C ++ 14. Add-pedantic-errors
e Clang fornecerão os erros apropriados que um compilador C ++ 11 puro forneceria.c ++ 11 não pode ser assim ...
idx
constante ou nãoseria bom se tivesse uma função cada
. Poderia ser isso se forçado em uma função
fonte
idx
éconst
. Se essa é apenas uma proposta padrão do C ++, não vejo como ela pertence na seção de respostas.