наследования и дикие шаблоны?
ты хочешь знать как работает std ?
вы смотрите на внутренности стандартной библиотеки, так что никакие референсы, кроме референса по вашей реализации стандартной библиотеки, в принципе не могут вам помочь
Здравствуйте. Как вы считаете, прав ли в данной ситуации кланг? https://godbolt.org/z/9P9o771bT учитывая https://eel.is/c++draft/temp.spec#temp.inst-3
любопытно, что он считает себя правым аж с версии 3.4 (более старые не умеют в iostream почему-то). мое собственное мнение пока в процессе формирования
Если хотите, можете посмотреть(или присоединиться) уже состоявшееся обсуждение: https://t.me/stdvector/588553
если коротко, то не прав, на мой взгляд если длинно, то (1) неявная инстанциация определения S<int> ведет к неявной инстанциации декларации шаблона operator[] на строке 13 (temp#inst-3.1). (2) далее вызов a[1] должен разрешиться в пользу декларации, инстанцированной из шаблона на строке 13 (temp#inst-10) (*), (3) что ведет к неявной инстанциации уже определения operator[] из того же шаблона (temp#inst-4), что (4) позволит компилятору вывести возвращаемый тип (dcl.spec.auto#general-12) * в том, что это возможно при помощи одной лишь декларации без вывода возвращаемого типа, я убедился полнотекстовым поиском return, dedu, definition и placeholder по главе [over] clang же ломается, как я понимаю, где-то между шагами 2 и 3, считая, что перегрузка не может разрешиться в функцию, возвращаемый тип которой не выведен, но он не может его вывести, пока не выберет ее и не инстанцирует определение пошерстил CWG issues, но ничего не нашел. но там столько, что я вполне мог что-то упустить меня сбило с толку, что этот шаблон на строке 13 одновременно и member template specialization (temp.expl.spec-15) с точки зрения класса, который для целей инстанциации определения класса является declared specialization (temp.spec#temp.expl.spec-1.9, temp.spec#temp.inst-1), но после нее становится обычным member function template, который требует неявной инстанциации всего. еще я не нашел, что такое инстанциация, когда не указывается, инстанциация это декларации или же определения. буду рад, если кто-то докажет, что я слепой
А в пункте 1 неявно инстанцируется декларация на строке 8 или 13 всё же? Спасибо за ваш подробный разбор :)
8 это всего лишь primary template, который ведет к специализации на строке 13
То есть декларация из основного шаблона вообще не инстанцируется? Ибо меня явно ввёл в заблуждение данный пример: https://eel.is/c++draft/temp#inst-example-3 Он, кстати, забавен тем, что несмотря на подпись ни в одном из компиляторов не падает
да, не инстанцируется, аналогично template<typename T> class A { /* def1 */ }; template<> class A<int> { /* def2 */ }; A<int> a; def1 вообще не интересен и его может вовсе не быть, если я все правильно понимаю
насчет примера: // OK, definition of C<void>::f is not instantiated at this point — это работает the implicit instantiation of the declarations, but not of the definitions, of the non-deleted class member functions из первого подпункта // error: redefinition of C<int>::g — это работает the implicit instantiation of the definitions of deleted member functions из второго подпункта
Да, но откуда там берётся инстанцировение определения удалённой функции, если на основной шаблон мы даже не смотрим?
здесь речь об инстанциации класса, а выше мы говорили об инстанциации шаблона функции-члена (когда выбирали между 8 и 13 строками). мне развить мысль или и так понятно?
Понятно, спасибо
А можно всё же разъяснения? :) Простите. Меня интересует сейчас в первую очередь этот пример из стандарта. Я просто не улавливаю логики. Более того, глядя на пример ниже, становится жутко от возможностей С++... #include <iostream> template<typename T> struct X { void foo() {} void bar() {} }; template<> void X<int>::bar() { std::cout << "bar\n"; } int main(int argc, char *argv[]) { X<int> x; x.bar(); x.foo(); } И непонятно, как это вообще работает, однако согласно https://eel.is/c++draft/temp#inst-3.1 это по идее, верно(?). Моё предположение было в том, что наличие такой частичной специализации метода как бы действительно инстанцирует весь класс с данным типом, создавая и все методы заодно, а т.к. мы создаём как бы перегрузку одного из методов, то рассматривается сигнатура данного метода. И в случае = delete метода для конкретно такой ситуации её определение становится частью объявления, и поэтому на основе несоответствия сигнатур такая частичная специализация метода должна отвергаться, однако если я правильно мыслю(?), то почему разрешено(?) переопределить в частичной специализации метод, с уже наличествующим определением? То есть оба этих определения существуют, но конкретная специализация создаётся именно с определением наиболее специализированного метода? И почему тогда всё же играет роль = delete, если можно было бы точно так же просто откинуть это определение?
в первую очередь хочу отметить, что ни раньше, ни сейчас речь не шла о частичных специализациях — только о полных. у меня есть представление, как они работают, но нам сейчас они вообще не нужны. у вас столько вопросов, что, полагаю, есть смысл разобрать следующий пример: #include <iostream> template<typename T> struct X { void foo() { std::cout << "foo in-class"; } // deleted __definition__ // declaration would be void bar() void bar() = delete; void baz() { std::cout << "baz in-class"; } void qux() = delete; }; // specialization 1 template<> void X<int>::foo() { std::cout << "foo spec"; } // specialization 2 template<> void X<int>::bar() { std::cout << "bar spec"; } int main() { X<int> x; // 1 x.foo(); // 2 x.bar(); // 3 x.baz(); // 4 x.qux(); // 5 } // specialization 3, IFNDR template<> void X<int>::qux() { std::cout << "qux spec"; } 0) к моменту компиляции main() компилятору известно следующее: определение шаблона класса A с определением всех членов, и declared specializations A<int>::foo() и A<int>::bar(), которые являются определяющими объявлениями (basic#def-2) 1) в точке 1 требуется определение специализации X<int>. в явном виде эта специализация ни объявлена, ни определена, поэтому определение генерируется согласно temp.spec#temp.inst-2 и следующего за ним пункта 3: template<> class X<int> { // declaration from spec 1 void foo(); // declaration from spec 2 void bar(); // 3.1, declaration void baz(); // 3.2, (deleted) definition void qux() = delete; }; зато в точке 1 компилятор знает о существовании специализаций 1 и 2 (их объявления требует temp.spec#temp.expl.spec-7.sentence-1), и использует их объявления в специализации класса. temp.spec#temp.inst-3 для них не работает ввиду temp.spec#temp.inst-11.sentence-1 (*) 2) в точке 2 нужно сначала разрешить перегрузку. множество кандидатов состоит из одного-единственного объявления X<int>::foo() в X<int>. выбор очевиден, полагаю. 3) после разрешения перегрузки требуется определение специализации X<int>::foo(). известно, что эта декларация пришла из declared specialization, поэтому определение берется оттуда. 4) с x.bar() ситуация полностью аналогична x.foo(), несмотря на то, что в шаблоне класса она является удаленной функцией. 5) в точке 4 про X<int>::baz() известно только объявление. так как оно было неявно сгенерировано в отсутствие declared specialization, то и определение будет неявно сгенерировано согласно temp.spec#temp.inst-4 6) в точке 5 про специализацию 3 ничего не известно, что нарушает требование, о котором шла речь в первом пункте. в ее отсутствие вызов в этой точке является ill-formed, потому что перегрузка разрешается в удаленную функцию X<int>::qux() = delete 7) на момент, когда о специализации 3 становится известно, определение X<int>::qux() = delete уже сгенерировано (п. 1), поэтому она является переопределением и нарушает ODR (*) вчера я жаловался на то, что не знаю, что такое инстанциация, так вот тут тоже не написано, об объявлениях ли речь или об определениях. наверное, и то том, и о другом. подтверждение, которое полагается на качество реализации в отсутствие альтернатив
Да, почему-то я везде написал о частичных, хотя говорил о полных, моя ошибка :) Спасибо за столь подробный разбор, здесь всё понятно. Но вот насчёт последнего вашего примера и примера из стандарта... Во-первых, я бы не доверял как раз реализациям, поскольку пример из стандарта, помеченный как error, они все скомпилировали. Впрочем, как я понял, это ifndr... Однако там нет причин, чтобы данный пример считался error. По разобранному же понятно, почему нет ошибки с C<void> c; в данном примере, ибо методы просто не инстанцируются за ненадобность. Когда же мы объявляем следующую полную специализацию для int, не для void ведь, у нас нет необходимости, чтобы это определение было доступно в какой-либо точке инстанцирования выше. Следовательно, в стандарте в данном примере ошибка или я опять что-то недопонял?
1) да, реализации вам ничего не должны, потому что нарушается ODR со всеми вытекающими 2) вот здесь в предпоследней строке кода (A<char>) похожий пример. можно еще и над ним медитировать 3) вернемся к тому примеру из стандарта, который вы не поняли. в объявлении специализации template<> C<int>::g() определение C<int> инстанцируется в процессе поиска имени C<int>::g. я перечитал basic.lookup вдоль и поперек, как и некоторые главы раздела стандарта про шаблоны, но не нашел прямого подтверждения, поэтому довольствуемся quality-of-implementation proof и здравым смыслом, что мы не может найти g в C<int>, если у нас нет последнего 4) в свою очередь неявное инстанцирование C<int> ведет к инстанциации deleted defintion C<int>::g() согласно temp.spec#temp.inst-3.2. таким образом еще до определения явной специализации C<int>::g() возникает неявный deleted definition из шаблона класса 5) фатальная ошибка вчерашнего опуса в том, что я разделил пункты 0 и 1. очень ожидаемо, что тут сказать
Обсуждают сегодня