Considere o seguinte exemplo ( snippet (0) ):
struct X
{
constexpr int get() const { return 0; }
};
void foo(const X& x)
{
constexpr int i = x.get();
}
int main()
{
foo(X{});
}
O exemplo acima é compilado com todas as versões g++
anteriores g++ 10.x
e nunca compiladas abaixo clang++
. A mensagem de erro é:
error: 'x' is not a constant expression 8 | constexpr int i = x.get(); |
exemplo ao vivo em godbolt.org
O erro meio que faz sentido, pois x
nunca é uma expressão constante no corpo de foo
:
X::get()
está marcadoconstexpr
e não depende do estado dex
;Mudar
const X&
paraconst X
faz com que o código seja compilado com todos os trechos do compilador (em godbolt.org) (1) .
Fica ainda mais interessante quando eu marcar X::get()
como static
( (no godbolt.org) trecho (2) ). Com essa alteração, todas as versões testadas g++
(incluindo o tronco) são compiladas, embora clang++
sempre falhem na compilação.
Então, minhas perguntas:
Está
g++ 9.x
correto ao aceitar o snippet (0) ?Todos os compiladores estão corretos ao aceitar o snippet (1) ? Se sim, por que a referência é significativa?
São
g++ 9.x
eg++ trunk
corretos ao aceitar o trecho (2) ?
x
infoo
não é uma expressão constante. Existe até um relatório de bug antigo (rejeitado informalmente) no clang por seu comportamento correto (enquanto o GCC tinha um bug real).Respostas:
Não.
Sim, eles estão.
Uma expressão constante não pode usar uma expressão id nomeando uma referência que não possui uma inicialização de expressão constante anterior ou que começou sua vida útil durante a avaliação de expressão constante. [expr.const] / 2,11 (o mesmo em C ++ 20 )
O mesmo não se aplica se você estiver nomeando uma variável que não é de referência sem envolver nenhuma conversão de lvalue para rvalue.
x.get()
refere-se apenas ax
lvalue e chama apenas umaconstexpr
função que na verdade não acessa nenhum membrox
, portanto não há problema.Não, porque a expressão ainda contém a subexpressão
x
que viola a regra mencionada acima.fonte