Я пытаюсь понять следующие фрагменты кода
Фрагмент №1
template <typename T>
struct A
{
static constexpr int VB = T::VD;
};
struct B : A<B>
{
};
Ни gcc9, ни clang9 не выдают здесь ошибку.
В. Почему этот код компилируется? Разве мы не являемся экземплярами A<B>
при наследовании от B? В B нет VD, поэтому не должен ли компилятор выдать ошибку здесь?
Фрагмент № 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;
};
В этом случае gcc9 компилируется нормально, но clang9 выдает ошибку, говорящую «Нет члена с именем AD в B».
В. Почему он компилируется с gcc9 / почему не компилируется с clang9?
Фрагмент № 3
template <typename T>
struct A
{
using TB = typename T::TD;
};
struct B : A<B>
{
using TD = int;
};
Здесь и clang9, и gcc9 выдают ошибку. gcc9 говорит "недопустимое использование неполного типа" struct B "".
В. Если структура B здесь неполная, то почему она не неполная во фрагменте № 2?
Компилятора флаги используются: -std=c++17 -O3 -Wall -Werror
. Заранее спасибо!!!
c++
templates
language-lawyer
Изменчивый побочный эффект
источник
источник
struct B
инстанцированииA
сB
?B
неполный ... Но не уверен, когда член должен бытьОтветы:
Я считаю, что это сводится к [temp.inst] / 2 (выделено мое):
и [temp.inst] / 9
Формулировка в стандарте, касающаяся неявного создания шаблона, оставляет много деталей открытыми для интерпретации. В общем, мне кажется, что вы просто не можете полагаться на части шаблона не создаются, если в спецификации указано иное. Таким образом:
Фрагмент №1
Вы создаете экземпляр
A<B>
. Но создание экземпляровA<B>
только создает экземпляры объявлений, а не определения его статических элементов данных.VB
никогда не используется таким способом, который потребовал бы определения для существования. Компилятор должен принять этот код.Фрагмент № 2
Как указал Jarod42, декларация
AB
содержит тип заполнителя. Мне кажется, что формулировка стандарта не совсем ясна в отношении того, что здесь должно происходить. Вызывает ли создание объявления статического члена данных, содержащего тип заполнителя, вычет типа заполнителя и, таким образом, представляет собой использование, которое требует определения статического члена данных? Я не могу найти в стандарте формулировку, которая бы четко говорила «да» или «нет». Таким образом, я бы сказал, что обе интерпретации здесь одинаково действительны, и, таким образом, GCC и Clang оба являются правильными ...Фрагмент № 3
Классовый тип является полным только в том месте , где вы достигнете закрытия
}
этого класса спецификатора [class.mem] / 6 . Таким образом,B
неполный во время неявной реализацииA<B>
во всех ваших фрагментах. Просто это было неважно для Сниппета № 1. В Snippet # 2 в результате clang выдал ошибкуNo member named AD in B
. Как и в случае с Snippet # 2, я не могу найти формулировку, когда именно будут созданы объявления псевдонимов членов. Однако, в отличие от определения статических членов-данных, не существует формулировок, которые бы явно предотвращали создание деклараций псевдонимов членов во время неявной реализации шаблона класса. Таким образом, я бы сказал, что поведение как GCC, так и Clang является действительной интерпретацией стандарта в этом случае ...источник
constexpr
член статических данных был только объявлением. C ++ 17 получилinline
переменные иconstexpr
подразумеваетinline
, и это делает определение элемента статических данных в теле определением.auto
случае правило гласит, что декларация должна быть декларацией инициализации. Это может иметь место только в том случае, если известно, что декларация является определением (насколько я знаю. Я уже давно не в юристе). В прошлом был похожий случай, и был добавлен eel.is/c++draft/temp.inst#2.sentence-3 , где декларация, являющаяся определением, создается как «известная как определение» без фактического создание определения.