Указатель как аргумент шаблона непигового типа

Отвечая на этот вопрос SO, я нашел в стандарте (уже С++ 03, все еще в С++ 11), что вы можете использовать только адреса как non -type, если они имеют форму & id-expression (плюс некоторые исключения).

Но я не мог ответить, почему это так.

14.3.2 Шаблон аргументов non-type [temp.arg.nontype]

Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:

[...]

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

(n3485, акцент мой)

Пример:

using TFoobar = int (*)();
template < TFoobar tp > struct foo_struct{};

int foobar() { return 42; }
constexpr TFoobar pFoobar = &foobar;

foo_struct < &foobar > o0; // fine
foo_struct < pFoobar > o1; // ill-formed

Я предполагаю, что это имеет какое-то отношение к фазам перевода, а именно, компилятор мало знает о адресах. Но почему это не разрешено? Не следует ли компилятору использовать что-то похожее на макроподстановку, чтобы заменить pFoobar на &foobar?

Ответ 1

Рассмотрим классы Foo<&X> и Foo<&Y>, как со статическим членом int Bar. Компонент должен иметь возможность указать, имеет ли ваша программа 1 или 2 объекта Bar. Теперь рассмотрим, что компоновщик также является стороной, наиболее вероятно ответственной за присвоение значений &X и &Y.

Посмотрите на стандарт снова. Как написано, компилятору не нужно связывать фактический адрес с компоновщиком. Вместо этого он передает id-expression. Компоненты уже вполне способны определить, совпадают ли два id-expression, даже до назначения им числового адреса.

Ответ 2

  • Он не может быть переменной, поскольку переменные устанавливаются только во время выполнения.
  • Это не может быть constexpr, так как значение адреса не может быть известно во время компиляции; в большинстве случаев он будет исправлен только после переноса до выполнения.
  • Теоретически это может быть арифметическое выражение (даже если оно не разрешено в стандарте), но в общем случае адреса элемента массива вы можете просто использовать &arr[i] вместо arr + i.