Estou tentando entender os seguintes trechos de código
Snippet # 1
template <typename T>
struct A
{
static constexpr int VB = T::VD;
};
struct B : A<B>
{
};
Nem o gcc9 nem o clang9 lançam um erro aqui.
P. por que esse código é compilado? Não estamos instanciando A<B>
ao herdar de B? Não há VD em B, então o compilador não deve lançar um erro aqui?
Snippet # 2
template <typename T>
struct A
{
static constexpr auto AB = T::AD; // <- No member named AD in B
};
struct B : A<B>
{
static constexpr auto AD = 0xD;
};
Nesse caso, o gcc9 compila bem, mas o clang9 gera um erro dizendo "Nenhum membro chamado AD em B".
P. Por que ele compila com o gcc9 / por que não compila com o clang9?
Snippet # 3
template <typename T>
struct A
{
using TB = typename T::TD;
};
struct B : A<B>
{
using TD = int;
};
Aqui, clang9 e gcc9 lançam um erro. gcc9 diz "uso inválido do tipo incompleto 'struct B'".
Q. Se a estrutura B está incompleta aqui, por que não está incompleta no trecho 2?
Bandeiras do compilador usado: -std=c++17 -O3 -Wall -Werror
. Desde já, obrigado!!!
c++
templates
language-lawyer
Efeito Colateral Mutável
fonte
fonte
struct B
instanciadoA
comB
?B
está incompleto ... Mas nãoRespostas:
Eu acredito que estes se resumem a [temp.inst] / 2 (grifo meu):
e [temp.inst] / 9
A redação do padrão referente à instanciação implícita de modelos deixa muitos detalhes abertos à interpretação. Em geral, parece-me que você simplesmente não pode confiar em partes de um modelo que não são instanciadas, a menos que a especificação o diga explicitamente. Portanto:
Snippet # 1
Você está instanciando
A<B>
. Mas a instanciaçãoA<B>
apenas instancia as declarações, não as definições de seus membros de dados estáticos.VB
nunca é usado de uma maneira que exija uma definição para existir. O compilador deve aceitar esse código.Snippet # 2
Conforme apontado por Jarod42, a declaração de
AB
contém um tipo de espaço reservado. Parece-me que a redação do padrão não é muito clara sobre o que deveria acontecer aqui. A instanciação da declaração de um membro de dados estático que contém um tipo de espaço reservado aciona a dedução do tipo de espaço reservado e, portanto, constitui um uso que requer a definição do membro de dados estáticos? Não consigo encontrar uma redação no padrão que diga claramente sim ou não a isso. Assim, eu diria que as duas interpretações são igualmente válidas aqui e, portanto, o GCC e o clang estão certos ...Snippet # 3
Um tipo de classe está completo apenas no ponto em que você alcança o fechamento
}
do especificador de classe [class.mem] / 6 . Portanto,B
fica incompleto durante a instanciação implícita deA<B>
em todos os seus snippets. Isso é irrelevante para o snippet nº 1. No Snippet # 2, o clang causou um erroNo member named AD in B
como resultado. Semelhante ao caso do Snippet # 2, não consigo encontrar quando exatamente as declarações de alias de membros seriam instanciadas. No entanto, diferentemente da definição de membros de dados estáticos, não há texto para impedir explicitamente a instanciação de declarações de alias de membro durante a instanciação implícita de um modelo de classe. Assim, eu diria que o comportamento do GCC e do clang é uma interpretação válida do padrão neste caso ...fonte
constexpr
membro de dados estáticos era apenas uma declaração. O C ++ 17 ganhouinline
variáveis econstexpr
implicainline
, e isso faz da declaração do membro de dados estáticos do corpo uma definição.auto
caso, a regra diz que a declaração deve ser uma declaração de inicialização. Isso só pode ser o caso se soubermos que a declaração é uma definição (tanto quanto eu sei ... estou fora da área de advogados há um tempo). No passado, havia um caso semelhante e eel.is/c++draft/temp.inst#2.sentence-3 foi adicionado, onde uma declaração que é uma definição é instanciada como "sendo conhecida por ser uma definição" sem realmente instanciando a definição.