Определение шаблона после использования
От: andrey.desman  
Дата: 03.04.18 20:07
Оценка:
#include <cstdio>

const char* name;

template<typename T>
void set_name(const char* n, T a);

void foo()
{
    set_name("test", 11);
}

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;
}


Почему этот код линкуется? Компилятор "собирает" запросы на инстанцирование?
И должен ли по стандарту?
Re: Определение шаблона после использования
От: watchmaker  
Дата: 03.04.18 20:51
Оценка: +1
Здравствуйте, andrey.desman, Вы писали:


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();
}
Re[2]: Определение шаблона после использования
От: andrey.desman  
Дата: 03.04.18 21:21
Оценка:
Здравствуйте, watchmaker, Вы писали:

AD>>Почему этот код линкуется? Компилятор "собирает" запросы на инстанцирование?

W>Шаблоны и инстанциирование тут не важны.
W>Это же обычный forward-declaration.
W>Это явно видно, если чуть модифицировать код так:

С форвардом все как раз просто и понятно. Декларация, потом определение. Известно, что вызывать, линкер найдет потом.
Тут же в точке определения шаблона не возникает определения функции как таковой. Ее надо инстанцировать по факту использования, но в месте использования еще нет определения.
Т.е. компилятор по факту производит инстанцирование уже в самом конце (файла, пространства имен?), а не в момент использования, и непонятно, это так и должно быть, или особенность компиляторов. На студии это тоже работает вроде как.
Re[3]: Определение шаблона после использования
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.04.18 22:02
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Т.е. компилятор по факту производит инстанцирование уже в самом конце (файла, пространства имен?), а не в момент использования, и непонятно, это так и должно быть, или особенность компиляторов. На студии это тоже работает вроде как.


Похоже, что оно так и работает. Не знаю, как там по стандарту, но иногда использовал форвард декларейшн для шаблонов, когда не обойтись было. В MSVC2005/2008 работает, как и в gcc тех же времен, мне хватало
Маньяк Робокряк колесит по городу
Re: Определение шаблона после использования
От: Constructor  
Дата: 03.04.18 22:26
Оценка: 6 (1)
Здравствуйте, 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")
Отредактировано 03.04.2018 22:36 Constructor . Предыдущая версия .
Re[3]: Определение шаблона после использования
От: watchmaker  
Дата: 03.04.18 22:41
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Тут же в точке определения шаблона не возникает определения функции как таковой. Ее надо инстанцировать по факту использования, но в месте использования еще нет определения.

AD>Т.е. компилятор по факту производит инстанцирование уже в самом конце (файла, пространства имен?), а не в момент использования, и непонятно, это так и должно быть, или особенность компиляторов.
Думаю, выделенный фрагмент должен дать ответ: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#993 — компилятору разрешено инстанциировать функцию в конце TU, а не непосредственно после её использования (когда ещё не доступно её определение).
Re[4]: Определение шаблона после использования
От: Videoman Россия https://hts.tv/
Дата: 04.04.18 12:31
Оценка:
Здравствуйте, 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
Re[2]: Определение шаблона после использования
От: N. I.  
Дата: 05.04.18 10:14
Оценка: 8 (1)
Constructor:

C>Де-юре этот код был некорректен до выхода C++11.


Откуда это следует?

C>См. Defect Report 993.


Какое отношение 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 в каком-то строгом порядке я ничего не вижу.
Re[3]: Определение шаблона после использования
От: Constructor  
Дата: 05.04.18 12:43
Оценка:
Здравствуйте, 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:

template<class T> void f(T); // declaration

void g() {
  f("Annemarie"); // call of f<const char*>
}
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 ]

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.