В какой момент происходит создание шаблона привязки?

Этот код взят из «языка программирования C ++» Бьярна Страуструпа (C.13.8.3 Binding of Instantiation Binding)

template <class T>
void f(T value)
{
g(value);
}

void g(int v);

void h()
{
extern g(double);
f(2);
}

И он упоминает:

Здесь точка создания экземпляра для f () находится непосредственно перед h (), поэтому
g (), вызываемая в f (), является глобальным g (int), а не локальным
г (дважды). Определение «точки инстанции» подразумевает, что
Параметр шаблона никогда не может быть связан с локальным именем или классом
член.

void h()
{
struct X {}; // local structure
std::vector<X> v; // error: can't use local structure as template parameter
}

Мои вопросы:

  1. Почему должен работать первый код? g() объявляется позже, и я действительно получаю ошибку с G ++ 4.9.2, что g не объявлено в тот момент.

  2. Extern G (двойной) — как это работает? поскольку возвращаемое значение не имеет значения в случае перегрузки функции, то мы можем пропустить его в предварительных объявлениях?

  3. точка создания экземпляра для f () находится непосредственно перед h () — Зачем? Разве не логично, что он будет создан, когда f(2) называется? Прямо там, где мы это называем, откуда g(double) будет в области уже.

  4. Определение «точки создания экземпляра» подразумевает, что параметр шаблона никогда не может быть привязан к локальному имени или члену класса — Изменилось ли это в C ++ 14? Я получаю ошибку с C ++ (G ++ 4.9.2), но не получаю ошибку с C ++ 14 (G ++ 4.9.2).

20

Решение

«В 1985 году было выпущено первое издание языка программирования C ++, которое стало окончательной ссылкой на язык, так как еще не было официального стандартаВики C ++ История Так что между C ++ 11 и C ++ 14 ничего не изменилось. Я могу предположить (и, пожалуйста, примите это с небольшим количеством соли), что это изменилось между «предварительной стандартизацией» и стандартизацией. Может быть, кто-то, кто лучше знает историю C ++, может пролить больше света здесь.

Что касается того, что на самом деле происходит:


Сначала давайте уберемся с простого:

extern g(double);

Это неверный C ++. Исторически, к сожалению, C допускал пропуск типа. В C ++ вы должны написать extern void g(double),


Далее давайте проигнорируем g(double) Перегрузка, чтобы ответить на ваш первый вопрос:

template <class T>
void f(T value)
{
g(value);
}

void g(int v);

int main()
{
f(2);
}

В C ++ есть печально известный двухфазный поиск имен:

  • На первом этапе, при определении шаблона, все независимые имена решены. Невыполнение этого требования является серьезной ошибкой;
  • Зависимые имена разрешаются на втором этапе при создании шаблона.

Правила немного сложнее, но в этом суть.

g зависит от параметра шаблона T так что проходит первый этап. Это означает, что если вы никогда не создаете экземпляр fкод компилируется просто отлично. На втором этапе f создается с T = int, g(int) сейчас ищется, но не найден:

17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup
g(value);
^
24 : note: in instantiation of function template specialization 'f<int>' requested here
f(2);
^
20 : note: 'g' should be declared prior to the call site
void g(int v);

Для произвольного имени g Чтобы пройти с летающими цветами у нас есть несколько вариантов:

  1. декларировать g ранее:
void g(int);

template <class T>
void f(T value)
{
g(value);
}
  1. приносить g в с T:
template <class T>
void f(T)
{
T::g();
}

struct X {
static void g();
};

int main()
{
X x;
f(x);
}
  1. приносить g в с T через ADL:
template <class T>
void f(T value)
{
g(value);
}

struct X {};

void g(X);

int main()
{
X x;
f(x);
}

Это, конечно, меняет семантику программы. Они предназначены для иллюстрации того, что вы можете и не можете иметь в шаблоне.


А почему ADL не находит g(int), но находит g(X):

§ 3.4.2 Поиск имени в зависимости от аргумента [basic.lookup.argdep]

  1. Для каждого типа аргумента T в вызове функции существует набор из нуля или более связанных пространств имен и набор из нуля или более
    связанные классы должны быть рассмотрены […]:

    • Если T является фундаментальным типом, то связанные с ним наборы пространств имен и классов являются пустыми.

    • Если T является типом класса (включая объединения), его ассоциированными классами являются: сам класс; класс, членом которого он является, если таковой имеется; а также
      его прямые и косвенные базовые классы. Его связанные пространства имен
      пространства имен, членами которых являются связанные с ним классы. […]


И, наконец, мы получаем, почему extern void g(double); внутри главное не найдено: прежде всего мы показали, что g(fundamental_type) найден тогда и только тогда объявлено до f определение. Итак, давайте сделаем это void g(X) внутри main, ADL находит это?

template <class T>
void f(T value)
{
g(value);
}

struct X{};int main()
{
X x;
void g(X);

f(x);
}

Нет. Потому что он не находится в том же пространстве имен, что и X (то есть глобальное пространство имен) ADL не может его найти.

Доказательство того g не в глобальном

int main()
{
void g(X);

X x;
g(x); // OK
::g(x); // ERROR
}

34: ошибка: в глобальном пространстве имен нет члена с именем «g»; ты имел ввиду
просто г?

13

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]