Chamando a função de membro `constexpr` através de referência - clang vs gcc

8

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.xe 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 xnunca é uma expressão constante no corpo de foo:

  • X::get()está marcado constexpre não depende do estado de x;

  • Mudar const X&para const Xfaz 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.xcorreto 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.xe g++ trunkcorretos ao aceitar o trecho (2) ?

Vittorio Romeo
fonte
constexpr é uma 'máscara' para um valor e uma const é uma variável que você informa que não será alterada. Valores não podem ser referenciados ou apontados, mas const sim. O membro da função get () é processado em tempo de compilação. Em outras palavras, é 0 para todos os casos. Como eu disse antes, os valores não podem ser referenciados ou apontados
TheArquitect
3
Eu acho que [expr.const] / 2,11 se aplica aqui, e xin foonã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).
dfri 28/02
3
Escreveu sobre isso recentemente, parece relevante: brevzin.github.io/c++/2020/02/05/constexpr-array-size
Barry
@ Barry nice, em seu artigo você também tem a referência ao relatório de bug do GCC correspondente (eu só consegui encontrar o clang rejeitado) que permitiu ao gcc 9.x aceitar o snippet 0, que foi corrigido desde então.
dfri 28/02

Respostas:

12

O g ++ 9.x está correto ao aceitar o snippet (0)?

Não.

Todos os compiladores estão corretos ao aceitar o snippet (1)? Se sim, por que a referência é significativa?

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 a xlvalue e chama apenas uma constexprfunção que na verdade não acessa nenhum membro x, portanto não há problema.

Os troncos g ++ 9.xe g ++ estão corretos ao aceitar o snippet (2)?

Não, porque a expressão ainda contém a subexpressão xque viola a regra mencionada acima.

noz
fonte
Relatórios de erros relevantes (rejeitados informalmente) no clang apontando a mesma correção no clang (sempre) que falha ao compilar o snippet 0.
dfri 28/02