Почему С++ 03 требует, чтобы параметры шаблона имели внешнюю связь?

Фон

В С++ 03 символы, используемые в качестве аргументов шаблона, должны иметь внешнюю связь; это ограничение было удалено в С++ 11, как описано в предыдущем вопросе:

В С++ 03 аргументы шаблона не могут иметь внутренней связи:

[C++03: 14.6.4.2/1]: Для вызова функции, который зависит от параметра шаблона, если имя функции является неквалифицированным идентификатором, но не идентификатором шаблона, функции-кандидаты обнаруживаются с использованием обычных правил поиска (3.4.1, 3.4. 2), за исключением того, что:

     
  •   
  • Для части поиска, использующей поиск неквалифицированного имени (3.4.1), найдены только объявления функций с внешней привязкой из контекста определения шаблона.   
  • Для части поиска с использованием связанных пространств имен (3.4.2) найдены только объявления функций с внешней связью, найденные либо в контексте определения шаблона, либо в контексте создания шаблона.  
     

[..]

Это было изменено (вопрос # 561: "Внутренние функции привязки в зависимом поиске имен" ) в С++ 11:

[C++11: C.2.6]: 14.6.4.2
   Изменить: разрешить зависимые вызовы функций с внутренней связью
   Обоснование: Слишком ограниченные, упрощают правила разрешения перегрузки.

в результате:

[C++11: 14.6.4.2/1]: Для вызова функции, зависящего от параметра шаблона, функции-кандидата найдены с использованием обычных правил поиска (3.4.1, 3.4.2, 3.4.3), за исключением того, что:

     
  •   
  • Для части поиска, использующей поиск неквалифицированного имени (3.4.1) или поиск квалифицированного имени (3.4.3), отображаются только объявления функций из контекста определения шаблона.  
  • Для части поиска с использованием связанных пространств имен (3.4.2) найдены только объявления функций, найденные в контексте определения шаблона или контекста создания шаблона.  
     

[..]

(Определите недостающую "внешнюю привязку" ).

Проблема № 561 ( "Функции внутренней привязки в зависимом поиске имен" ), предложение, которое привело к тому, что ограничение было удалено в C + +11, спрашивает:

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

С более поздним ответом:

Консенсус группы заключался в том, что [..] функции внутренней связи должны быть найдены путем поиска (хотя они могут приводить к ошибкам, если они выбраны с помощью разрешения перегрузки).


Вопрос

Каково было первоначальное практическое обоснование ограничения?

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

Это только то, что "[функции внутренней связи] могут приводить к ошибкам, если они выбраны с помощью разрешения перегрузки", и что в 2000-м мнении изменилось, насколько это важно? Или что-то изменилось, возможно, как косвенное следствие новой формулировки в другом месте для другой функции С++ 11?

Ответ 1

Я подозреваю, что это связано с печально известной функцией шаблона export на С++ 98. Думаю об этом. После того, как вы разрешите возможность определения шаблонов, входящих в отдельные единицы перевода, но все еще не по-настоящему скомпилированные до тех пор, пока не будут указаны аргументы шаблона (т.е. Экземпляр шаблона), вы попадаете в эту сумеречную зону, где TU с определением шаблона и ТУ с инстанцированием должны подчиняться правилам линкерной видимости (т.е. модели разделения), разделяя их контексты с точки зрения разрешения перегрузки. Решение этой проблемы состоит в том, чтобы разрешить только функции с внешней связью в зависимом поиске имен.

Вот пример. Одна из менее известных "особенностей" экспортируемых шаблонов заключается в том, что в шаблоне TU можно было бы использовать некоторые функции или классы с внутренней связью (т.е. Помеченные static или в неименованном пространстве имен). Что делать, если TU с экземпляром имеет функцию внутренней связи, которая была бы двусмысленной или, возможно, исключаемой той, что была в шаблоне TU? Что-то вроде сюрреалистической проблемы, я знаю, это странный мир экспортируемых шаблонов. Единственный способ избежать очень удивительного поведения - исключить все функции внутренней связи из поисковых запросов. Учтите также, что никто не имел четкого представления о том, как на самом деле реализовать экспортированные шаблоны, и, возможно, это будет невозможно реализовать без этого ограничения.

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

Вот конкретный пример:

// in exptemp.h
export template <typename T> bool is_valid(T value);

// in exptemp.cpp
namespace {
  bool is_space(char c) {
    return (c == ' ') || (c == '\t');
  };
};

template <typename T>
bool is_valid(T value) {
  return is_space(value);
};

// in test.cpp
#include "exptemp.h"

namespace {
  bool is_space(char c) {
    return (c == ' ') || (c == '\t') || (c == '\n');
  };
};

int main() {
  char c = '\n';
  return is_valid(c);   // will this return 0 or 1 ?!?!?
};

Ответ 2

Насколько я знаю, он чисто исторический - кажется, он был первоначально запрещен, потому что сглаживание имени скрипта было недостаточно для правильной обработки.

В какой-то момент Энтони Уильямс писал статью, предлагающую, чтобы это было разрешено, и рассказывали, как это сделать, - но AFAIK, что бумага никогда не принималась, и ее требования не редактировались в стандарте. Я подозреваю, что столько же времени, сколько и все остальное. Это было предложено в 2001 году, поэтому то, над чем они работали в то время (С++ 2003), не предназначалось для добавления много нового материала, и к тому времени, когда они начали серьезно работать на С++ 11, похоже, это было в основном забыто.