Быстрое напоминание, чтобы было понятно, о чем мы говорим:
int const a; // illegal
int const a = 42; // definition, internal linkage
extern int const a; // declaration, external linkage
extern int const a = 42; // definition , external linkage
Обратите внимание, что без const
первые два объявления выше оба
определения с внешней связью. Это не ортогональное, а
не очень интуитивно, но это то, что говорят текущие правила.
Проблема с постоянным внешним связыванием заключается в том, что
только одно определение объекта с внешней связью и с одним
исключение, только определение может иметь инициализатор. Это означает, что
для const с внешней связью фактическое значение (необходимо, если
const должен использоваться в постоянном выражении) может быть виден только в одном
переводческий блок. Это, вероятно, мотивация для предоставления const
внутренняя связь по умолчанию.
Конечно, это не вызывает никаких проблем с шаблонами, по крайней мере
теоретически; следующий заголовок имеет неопределенное поведение, если это
в том числе в нескольких переводческих единицах:
#include <std::vector>
int const fixedValue = 42;
inline void insertFixedValue( std::vector<int>& dest )
{
dest.push_back( fixedValue );
}
Стандарт гласит, что не только встроенные функции и шаблоны должны иметь
идентичная последовательность токенов, но все символы должны связываться
к одному и тому же объекту в каждой единице перевода, или есть нарушение
одно определение правила. А так как fixedValue
не имеет внешнего
связь, есть уникальный экземпляр в каждой единице перевода. (Есть
исключение , если символ относится к const
объекту и
немедленное преобразование lvalue в rvalue. поскольку
std::vector<int>::push_back
принимает аргумент в качестве ссылки, однако,
нет непосредственного преобразования lvalue в rvalue, и мы получаем неопределенное
поведение.)
И, конечно же, любой с шаблоном:
template <int& r> ...
также не может создать экземпляр с fixedValue
.
Причина внутренней связи, конечно, историческая. Сегодня,
Компиляторы должны поддерживать такие вещи, как:
struct X
{
static int const a = 42; // declaration!!!, external linkage
};
и всевозможные дубликаты определений для функций. Это было бы
относительно тривиально расширить правила, позволяющие инициализаторам на
объявление в классе переменных в области имен, чтобы дать
что-то вроде:
int const a; // illegal
int const a = 42; // definition, external linkage
extern int const a; // declaration, external linkage
extern int const a = 42; // declaration, external linkage
Это восстановит ортогональность (даже если это потребует дополнительного набора текста). Это
также нарушит почти весь существующий код.
Другой альтернативой будет обработка const
определений переменных
точно так же, как шаблоны функций обрабатываются сегодня: вы можете иметь несколько
определения, но все они должны быть идентичны. Это, вероятно, позволит избежать
большинство, если не все, поломка кода.