Так ведь это "глубокое вникание" и происходит сейчас, когда парсится non-template определение класса. Вот если при этом парсинге возникли ошибки, и все они - типа "incomplete class Foo" или являющиеся следствиями из них (если хочется упростить, то "если при этом парсинге возникли ошибки, и среди них есть типа "incomplete class Foo"), то
делаем список упомянутых неполных классов,
парсим "template<список со словом class перед каждым именем>",
парсим текущее определение класса, подменяя исходное имя класса на что-то уникальное,
если всё равно есть ошибки, выдаем полученные при первой итерации парсинга, иначе парсим using исходное_имя_класса = уникальное_имя<список>;
Такая магия (которой и так много в С++) обычно ни к чему хорошему не приводит. Начиная с того, что класс неожиданно окажется темплейтом, а значит его методы должны быть видны при компиляции. Типа
// bar.h class Bar { // implicit template public: void doSomething(); };
С какого бы рожна class Bar { public: void doSomething(); }; вдруг стал implicit template, если его декларация и так отлично компилируется? Вы, похоже, так и не поняли, чего именно я хочу.
Ну зачем сразу выбирать вариант "собеседник ни черта не понял" :) Отвечаю по порядку.
> С какого бы рожна class Bar вдруг стал implicit template,
Я писал в предположении, что class Bar содержит рекурсивные определения, которые, по вашему механизму, превращают его в неявный template. Я их просто не вписал, посчитав, что комментария достаточно. Ну вот допутим есть в нем вот это
Foo foo1, foo2;
class Bar { // implicit template public: void doSomething(); Foo foo1, foo2; };
Раз Bar теперь template, то его методы обязаны быть видны при компиляции всех использований. А раньше этого не было.
> Вы, похоже, так и не поняли, чего именно я хочу.
Я думаю, что понял общую идею. Если при компиляции класса Bar возникают ошибки, проистекающее от того, что класс Foo не определен, превращаем Bar в неявный темплейт Bar. Дальше будет много интересного, но это уже другая история :) И, кстати, вряд ли это позволит превратить Foo foo1, foo2; в vector, а если позволит, то ой. Но это уже третья история :)
template < typename T> struct BarT { T foo1, foo2; void doSomething(); }; // !!! using Bar = BarT< Foo>; // !!!
struct Foo { std::vector< Bar> bar; };
template< > void BarT< Foo>::doSomething() { std::cout << "Something\n"; } // Демонстрация, что компилируется и работает int main() { Bar bar; bar.doSomething(); }
компилируется и работает. Всего-то и надо, что при превращении класса в template или требовать, чтобы тела outlined методов определялись после доопределения необходимых классов, или сохранять их синтаксические деревья до этого момента, и только после этого компилировать.
Ну... Во-первых, с настоящими темплейтами популярные компиляторы так не делает, значит это либо нетривиально, либо очень медленно. Они требуют, чтобы тело метода было в том же логическом compilation unit (т.е. исходник + инклюды), что и использование.
Во-вторых, out of line метод может быть определен в библиотеке, распространяемой в виде двоичного файла + хедеры, и его синтаксическое дерево будет недоступно. Чтобы оно стало доступно, придется перекомпилировать все библиотеки новым компилятором. Это резко замедлит adoption.
В-третьих, начнутся забавные проблемы с shared objects. Сейчас уже слишком поздно, чтобы я мог подробно описать какие, но они точно будут :D
Сегодня обычные классы и темплейты живут как бы в разных мирах и подчиняются абсолютно разным правилам. Это в каком-то смысле два разных языка: две разные системы представления двоичного кода, две разные системы диагностики ошибок, и т.п. Неявный перевод классов из обычной системы в мир темплейтов возможен, но это будет еще одна мина.
О том и речь, что это получается не "настоящий" темплейт, а всегда мгновенно специализированный, причем с единственной специализацией, что существенно упрощает дело.
Если этих useBar() - приличное количество, и у каждой по несколько параметров, это вынесение будет бОльшим извратом, чем объявление Bar через template.
Я не об этом. Лукап в месте дефинишена и в месте инстанциации в общем случае приводит к разным результатам. Вы меняете array на vector, и у вас из-за этого меняется семантика. Я немного сбивчиво пишу, это проще объяснить многословно, размахивая руками.
Reply
// bar.h
class Bar { // implicit template
public:
void doSomething();
};
// bar.cpp
void Bar::doSomething() {... stuff... };
// usingBar.cpp
Bar b; // does not compile -- what are the template arguments?
Bar b; // OK
b.doSomething(); // the method is undefined
Далее, будут всякие приколы с template parameter inference:
class Bar { // implicit template on Foo
public:
Bar(const Foo& theFoo) {...}
};
class Foo {
public:
Foo(int arg) {...}
};
Bar bla(42); // if not template, a temporary Foo is constructed from 42
// if template, we are dealing with Bar now
Reply
Reply
Отвечаю по порядку.
> С какого бы рожна class Bar вдруг стал implicit template,
Я писал в предположении, что class Bar содержит рекурсивные определения, которые, по вашему механизму, превращают его в неявный template. Я их просто не вписал, посчитав, что комментария достаточно. Ну вот допутим есть в нем вот это
Foo foo1, foo2;
class Bar { // implicit template
public:
void doSomething();
Foo foo1, foo2;
};
Раз Bar теперь template, то его методы обязаны быть видны при компиляции всех использований. А раньше этого не было.
> Вы, похоже, так и не поняли, чего именно я хочу.
Я думаю, что понял общую идею. Если при компиляции класса Bar возникают ошибки, проистекающее от того, что класс Foo не определен, превращаем Bar в неявный темплейт Bar. Дальше будет много интересного, но это уже другая история :) И, кстати, вряд ли это позволит превратить Foo foo1, foo2; в vector, а если позволит, то ой. Но это уже третья история :)
Reply
#include < iostream>
#include < vector>
struct Foo;
template < typename T> struct BarT { T foo1, foo2; void doSomething(); }; // !!!
using Bar = BarT< Foo>; // !!!
struct Foo { std::vector< Bar> bar; };
template< > void BarT< Foo>::doSomething() { std::cout << "Something\n"; }
// Демонстрация, что компилируется и работает
int main() {
Bar bar;
bar.doSomething();
}
компилируется и работает. Всего-то и надо, что при превращении класса в template или требовать, чтобы тела outlined методов определялись после доопределения необходимых классов, или сохранять их синтаксические деревья до этого момента, и только после этого компилировать.
Reply
Во-вторых, out of line метод может быть определен в библиотеке, распространяемой в виде двоичного файла + хедеры, и его синтаксическое дерево будет недоступно. Чтобы оно стало доступно, придется перекомпилировать все библиотеки новым компилятором. Это резко замедлит adoption.
В-третьих, начнутся забавные проблемы с shared objects. Сейчас уже слишком поздно, чтобы я мог подробно описать какие, но они точно будут :D
Сегодня обычные классы и темплейты живут как бы в разных мирах и подчиняются абсолютно разным правилам. Это в каком-то смысле два разных языка: две разные системы представления двоичного кода, две разные системы диагностики ошибок, и т.п. Неявный перевод классов из обычной системы в мир темплейтов возможен, но это будет еще одна мина.
Reply
Reply
struct Bar;
struct Foo {
std::vector bar;
void useBar();
};
struct Bar {
Foo foo1, foo2;
};
inline void Foo::useBar() {
bar[0].foo1 = Foo();
}
Reply
Reply
Reply
Изменение семантики проще всего объяснить на примере. Если его удастся создать, конечно.
Reply
Leave a comment