Пытаясь понять шаблоны и поиск имени

9

Я пытаюсь понять следующие фрагменты кода

Фрагмент №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. Заранее спасибо!!!

Изменчивый побочный эффект
источник
@xception Не struct Bинстанцировании Aс B?
эффект
clang9 выдает ошибку «Нет члена с именем AD в B» . как Bнеполный ... Но не уверен, когда член должен быть
создан
@MutableSideEffect о, да, мой плохой, прочитайте это как шаблон :(
xception
@ Jarod42 тогда почему GCC компилируется нормально?
эффект
1
Я пометил этот вопрос как «нуждающийся в большем внимании», и этот вопрос действительно содержит более одного вопроса (отсюда мой вывод), так почему мой флаг неверен?
Доминик

Ответы:

4

Я считаю, что это сводится к [temp.inst] / 2 (выделено мое):

Неявная реализация специализации шаблона класса вызывает неявную реализацию объявлений, но не определений , аргументов по умолчанию или спецификаторов noexcept для функций-членов класса, классов-членов, перечислений членов-областей, статических членов-данных , шаблонов-членов и друзья; [...]

и [temp.inst] / 9

Реализация не должна неявно создавать экземпляр […] статического члена данных шаблона класса […], если только такое создание не требуется.

Формулировка в стандарте, касающаяся неявного создания шаблона, оставляет много деталей открытыми для интерпретации. В общем, мне кажется, что вы просто не можете полагаться на части шаблона не создаются, если в спецификации указано иное. Таким образом:

Фрагмент №1

В. Почему этот код компилируется? Разве мы не создаем экземпляр A при наследовании от B? В B нет VD, поэтому не должен ли компилятор выдать ошибку здесь?

Вы создаете экземпляр A<B>. Но создание экземпляров A<B>только создает экземпляры объявлений, а не определения его статических элементов данных.VBникогда не используется таким способом, который потребовал бы определения для существования. Компилятор должен принять этот код.

Фрагмент № 2

В. Почему он компилируется с gcc9 / почему не компилируется с clang9?

Как указал Jarod42, декларация AB содержит тип заполнителя. Мне кажется, что формулировка стандарта не совсем ясна в отношении того, что здесь должно происходить. Вызывает ли создание объявления статического члена данных, содержащего тип заполнителя, вычет типа заполнителя и, таким образом, представляет собой использование, которое требует определения статического члена данных? Я не могу найти в стандарте формулировку, которая бы четко говорила «да» или «нет». Таким образом, я бы сказал, что обе интерпретации здесь одинаково действительны, и, таким образом, GCC и Clang оба являются правильными ...

Фрагмент № 3

В. Если структура B здесь неполная, то почему она не неполная во фрагменте № 2?

Классовый тип является полным только в том месте , где вы достигнете закрытия }этого класса спецификатора [class.mem] / 6 . Таким образом, Bнеполный во время неявной реализации A<B>во всех ваших фрагментах. Просто это было неважно для Сниппета № 1. В Snippet # 2 в результате clang выдал ошибку No member named AD in B. Как и в случае с Snippet # 2, я не могу найти формулировку, когда именно будут созданы объявления псевдонимов членов. Однако, в отличие от определения статических членов-данных, не существует формулировок, которые бы явно предотвращали создание деклараций псевдонимов членов во время неявной реализации шаблона класса. Таким образом, я бы сказал, что поведение как GCC, так и Clang является действительной интерпретацией стандарта в этом случае ...

Майкл Кензел
источник
Спасибо. В этом случае мы инициализируем элемент статических данных в теле. Является ли инициализация частью объявления или частью определения статического члена данных? У меня сложилось впечатление, что если инициализация внутри тела, то это часть объявления статического члена данных. Если это часть объявления, то ваша первая цитата требует немедленной реализации как части неявной реализации окружения шаблона класса.
Йоханнес Шауб -
Я посмотрел в спецификации, и кажется, что есть разница между C ++ 14 и C ++ 17 здесь. В C ++ 14 constexprчлен статических данных был только объявлением. C ++ 17 получил inlineпеременные и constexprподразумевает inline, и это делает определение элемента статических данных в теле определением.
Йоханнес Шауб -
eel.is/c++draft/dcl.spec.auto#4.sentence-2 говорит: «Тип переменной, объявленной с использованием типа заполнителя, выводится из ее инициализатора. Это использование разрешено в объявлении инициализации ([dcl. init]) переменной. " Поэтому я бы сказал, что определение статического члена данных является обязательным, поскольку он содержит инициализатор.
Йоханнес Шауб -
@ JohannesSchaub-litb Спасибо за внимание! Я тоже интересовался этими вопросами, но не смог найти формулировки, которая была бы убедительной. Что касается инициализации и определения, рассмотрим определение функции-члена внутри определения шаблона класса. Такое определение также является декларацией, и других деклараций нет. Тем не менее, неявное создание экземпляра шаблона класса будет только создавать экземпляр объявления, но не определение функции-члена. Почему то же самое не относится к статическим элементам данных?
Майкл Кензел
если нет ничего, что требует определения, определение не создается. Но в этом autoслучае правило гласит, что декларация должна быть декларацией инициализации. Это может иметь место только в том случае, если известно, что декларация является определением (насколько я знаю. Я уже давно не в юристе). В прошлом был похожий случай, и был добавлен eel.is/c++draft/temp.inst#2.sentence-3 , где декларация, являющаяся определением, создается как «известная как определение» без фактического создание определения.
Йоханнес Шауб -