AD>Почему этот код линкуется? Компилятор "собирает" запросы на инстанцирование?
Шаблоны и инстанциирование тут не важны.
Это же обычный forward-declaration.
Это явно видно, если чуть модифицировать код так:
#include <cstdio>
const char* name;
//template<typename T>void set_name(const char* n, int a);
void foo()
{
set_name("test", 11);
}
//template<typename T>void set_name(const char* n, int a)
{
name = n;
}
int main(int argc, char* argv[])
{
foo();
}
Здравствуйте, watchmaker, Вы писали:
AD>>Почему этот код линкуется? Компилятор "собирает" запросы на инстанцирование? W>Шаблоны и инстанциирование тут не важны. W>Это же обычный forward-declaration. W>Это явно видно, если чуть модифицировать код так:
С форвардом все как раз просто и понятно. Декларация, потом определение. Известно, что вызывать, линкер найдет потом.
Тут же в точке определения шаблона не возникает определения функции как таковой. Ее надо инстанцировать по факту использования, но в месте использования еще нет определения.
Т.е. компилятор по факту производит инстанцирование уже в самом конце (файла, пространства имен?), а не в момент использования, и непонятно, это так и должно быть, или особенность компиляторов. На студии это тоже работает вроде как.
Здравствуйте, andrey.desman, Вы писали:
AD>Т.е. компилятор по факту производит инстанцирование уже в самом конце (файла, пространства имен?), а не в момент использования, и непонятно, это так и должно быть, или особенность компиляторов. На студии это тоже работает вроде как.
Похоже, что оно так и работает. Не знаю, как там по стандарту, но иногда использовал форвард декларейшн для шаблонов, когда не обойтись было. В MSVC2005/2008 работает, как и в gcc тех же времен, мне хватало
Здравствуйте, andrey.desman, Вы писали:
AD>Почему этот код линкуется? Компилятор "собирает" запросы на инстанцирование? AD>И должен ли по стандарту?
Де-юре этот код был некорректен до выхода C++11. См. Defect Report 993.
На данный момент место в стандарте, описывающее происходящее, выглядит так (см. главу Point of instantiation [temp.point], абзацы 1 и 8 в актуальной версии стандарта; выделение жирным мое):
1 For a function template specialization, a member function template specialization, or a specialization for a member function or static data member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization and the context from which it is referenced depends on a template parameter, the point of instantiation of the specialization is the point of instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.
...
8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one-definition rule (6.2), the program is ill-formed, no diagnostic required.
Т.е. имеем следующую картину:
#include <cstdio>
const char* name;
template<typename T>
void set_name(const char* n, T a);
void foo()
{
set_name("test", 11);
}
// первая точка инстанцирования специализации set_name<int>
// (в соответствии с абзацем 1, она "follows the namespace scope declaration or definition that refers to the specialization")template<typename T>
void set_name(const char* n, T a)
{
name = n;
}
const char* get_name()
{
return name;
}
int main(int argc, char* argv[])
{
foo();
printf("%s\n", get_name());
return 0;
}
// вторая точка инстанцирования специализации set_name<int>
// (в соответствии с абзацем 8, "for any such specialization that has a point of instantiation within the translation unit,
// the end of the translation unit is also considered a point of instantiation")
Здравствуйте, andrey.desman, Вы писали:
AD>Тут же в точке определения шаблона не возникает определения функции как таковой. Ее надо инстанцировать по факту использования, но в месте использования еще нет определения. AD>Т.е. компилятор по факту производит инстанцирование уже в самом конце (файла, пространства имен?), а не в момент использования, и непонятно, это так и должно быть, или особенность компиляторов.
Думаю, выделенный фрагмент должен дать ответ: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#993 — компилятору разрешено инстанциировать функцию в конце TU, а не непосредственно после её использования (когда ещё не доступно её определение).
Здравствуйте, watchmaker, Вы писали:
W> ...компилятору разрешено инстанциировать функцию в конце TU, а не непосредственно после её использования (когда ещё не доступно её определение).
Microsoft всегда это мог. Более того, вот это он тоже компилирует, без всяких forward declaration (естественно только в рамках одного файла):
#include <iostream>
template <typename Type>
int fun1(int n)
{
return fun2<Type>(n);
}
template <typename Type>
int fun2(int n)
{
return 0;
}
int main() {
return fun1<int>(6);
}
Я этим долго пользовался, пока не узнал что это не по стандарту и, оказывается, нужен froward declaration
Какое отношение points of instantiation имеют к данному вопросу? В стандарте C++03 (или каком-нибудь другом) где-то сказано, что какие-то points of instantiation должны располагаться именно после определения соответствующего шаблона?
Про необходимость определения шаблона в той же единице трансляции, где производится неявное инстанцирование, там говорится следующее:
C++03 [temp]/8:
A non-exported template must be defined in every translation unit in which it is implicitly instantiated (14.7.1), unless the corresponding specialization is explicitly instantiated (14.7.2) in some translation unit; no diagnostic is required.
Про необходимость следования template definition & points of instantiation в каком-то строгом порядке я ничего не вижу.
Здравствуйте, N. I., Вы писали:
C>>См. Defect Report 993.
NI>Какое отношение points of instantiation имеют к данному вопросу?
Похоже, что никакого. Они имеют отношение к разрешению имен в шаблонах (точнее, к разрешению dependent names).
NI>В стандарте C++03 (или каком-нибудь другом) где-то сказано, что какие-то points of instantiation должны располагаться именно после определения соответствующего шаблона?
Нашел только связь между определением шаблона класса и точками инстанцирования (см. главу Implicit instantiation [temp.inst], абзац 1 в актуальной версии стандарта):
If a class template has been declared, but not defined, at the point of instantiation (17.7.4.1), the instantiation yields an incomplete class type (6.9). [ Example:
template<class T> class X;
X<char> ch; // error: incomplete type X<char>
— end example ]
NI>Про необходимость определения шаблона в той же единице трансляции, где производится неявное инстанцирование, там говорится следующее:
NI>C++03 [temp]/8: NI>
A non-exported template must be defined in every translation unit in which it is implicitly instantiated (14.7.1), unless the corresponding specialization is explicitly instantiated (14.7.2) in some translation unit; no diagnostic is required.
NI>Про необходимость следования template definition & points of instantiation в каком-то строгом порядке я ничего не вижу.
Да, я тоже не смог найти. Вообще, про неявное инстанцирование шаблонов функций написано удивительно мало (см. главу Implicit instantiation [temp.inst], абзац 4):
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist.
С другой стороны, в главе о разрешении перегрузок шаблонов функций есть такие слова (см. главу Overload resolution [temp.over], абзац 5):
Only the signature of a function template specialization is needed to enter the specialization in a set of candidate functions. Therefore only the function template declaration is needed to resolve a call for which a template specialization is a candidate. [ Example:
The call of f is well-formed even if the template f is only declared and not defined at the point of the call. The program will be ill-formed unless a specialization for f<const char*>, either implicitly or explicitly generated, is present in some translation unit. — end example ]