Почему явная реализация шаблона не нарушает ODR? - PullRequest
0 голосов
/ 05 октября 2018

Этот вопрос возник в контексте этого ответа .

Как и следовало ожидать, этот блок перевода не компилируется:

template <int Num> int getNum() { return Num; }
template int getNum<0>();
template int getNum<0>();  // error: duplicate explicit instantiation of 'getNum<0>'
int main() { getNum<0>(); return 0; }

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

// decl.h
template <int Num> int getNum() { return Num; }

// a.cc
#include <decl.h>
template int getNum<0>();

// b.cc
#include <decl.h>
template int getNum<0>();
int main() { getNum<0>(); return 0; }

Я этого не ожидал.Я предположил, что несколько явных экземпляров шаблона с одинаковыми параметрами могут нарушить ODR, но, похоже, это не так.Это, однако, не помогает:

// decl.h
template <int Num> int getNum();

// a.cc
#include "decl.h"
template <> int getNum<0>() { return 0; }

// b.cc
#include "decl.h"
template <> int getNum<0>() { return 0; }
int main() { getNum<0>(); return 0; }

Пользователь Олив услужливо указал мне на этот соответствующий параграф в стандарте , но я все еще несколько смущен этим,поэтому я надеялся, что кто-то сможет объяснить в более простых терминах логику, лежащую в основе этого (например, что следует или не следует рассматривать как нарушение ODR и почему мои ожидания были неверными).

РЕДАКТИРОВАТЬ:

В качестве еще одного примера, вот программа, разделенная на две единицы, которая корректно компилируется, но при этом дает удивительно неожиданные результаты:

// a.cc
template <int Num> int getNum() { return Num + 1; }
template int getNum<0>();

// b.cc
#include <iostream>
template <int Num> int getNum() { return Num; }
template int getNum<0>();
int main() { std::cout << getNum<0>() << std::endl; return 0; }

Вывод:

1

В этом случае удаление явногоШаблон экземпляров выдает 0.Я знаю, что наличие двух шаблонов с разными определениями не является распространенным случаем, но я думал, что ODR был строго применен, чтобы избежать такого рода проблем.

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

Как явная специализация, так и явное определение экземпляров будут нарушать ODR в зависимости от контекста, который они используют, и значения сущностей, которые они генерируют.

Следующее объясняет первый и третий случай и почему они нарушают ODR с NDR [temp.spec]/5

Для данного шаблона и данного набора аргументов шаблона,

  • (5.1) явное определение экземпляра должно появиться в программе не более одного раза,

  • (5.2) явное определение должно быть определено не более одного разав программе (в соответствии с 6.2) шаблоны функций [...]

могут иметь разные точки создания экземпляров как в одной и той же единице перевода, где они определены, так и в других.В переводимых единицах эти специализации гарантированно не нарушают ODR, если значение этой специализации одинаково во всех точках реализации.

, поскольку [temp.point]/6

Явное определение экземпляра является точкой создания для специализации или специализаций, указанных явным созданием.

и [temp.point]/8

[...] Если две разные точки инстанцирования придают специализации шаблона разные значения в соответствии с правилом единого определения (6.2), программа некорректна, диагностика не требуется.

второй случай не нарушает ODR, потому что значение экземпляров в этих TU одинаково.

// decl.h
template <int Num> int getNum() { return Num; }

// a.cc
#include <decl.h>
template int getNum<0>();

// b.cc
#include <decl.h>
template int getNum<0>();
int main() { getNum<0>(); return 0; }

Но последнее, безусловно, недопустимо (нарушать ODR NDR), потому что даже шаблоны функцийимеют одинаковые подписи, экземпляры из которых будут иметь разные значения.Вы не можете передать полученный результат, стандарт не гарантирует поведение, когда происходит это нарушение.

// a.cc
template <int Num> int getNum() { return Num + 1; }
template int getNum<0>();

// b.cc
#include <iostream>
template <int Num> int getNum() { return Num; }
template int getNum<0>();
int main() { std::cout << getNum<0>() << std::endl; return 0; }
0 голосов
/ 05 октября 2018

Эврика!Наконец, я попадаю на соответствующий абзац, [temp.spec] / 5

Для данного шаблона и заданного набора аргументов шаблона,

  • (5.1) явное определение экземпляра должно появляться не более одного раза в программе,

  • (5.2) явное определение должно быть определено не более одного раза в программе, как указано в [basic.def.odr] и

  • (5.3) как явное создание экземпляра, так и объявление явной специализации не должны появляться в программе, если явное создание экземпляра не следует за объявлением явной специализации.

Реализация не требуется для диагностики нарушения этого правила.

Таким образом, явное определение экземпляра шаблона (неявное создание экземпляра) может вызвать нарушение ODR, диагностика не требуется (и, по крайней мере, gcc).и цепочки инструментов clang-ld не производят диагностику)

...