Адрес глобальной переменной constexpr?

Рассмотрим следующее

struct dummy{};

dummy d1;
dummy d2;

template<dummy* dum>
void foo()
{
    if (dum == &d1)
        ; // do something
    else if (dum == &d2)
        ; // do something else
}

Теперь можно вызвать foo следующим образом

foo<&d1>();
foo<&d2>();

и все работает так, как ожидалось. Но следующее не

constexpr dummy* dum_ptr = &d1;
foo<dum_ptr>();

С этой ошибкой в ​​Visual Studio

ошибка C2975: dum_ptr: неверный аргумент шаблона для foo, ожидаемое выражение константы времени компиляции

Пока это работает

constexpr dummy& dum_ref = d1;
foo<&dum_ptr>();

В визуальной студии, но не в g++, из-за

Примечание: сбой аргумента шаблона/замена:
error: & dum_ref не является допустимым аргументом шаблона для dummy*, потому что это не адрес переменной

foo<&dum_ref>();

EDIT:
Поскольку С++ 17, std::addressof помечен как constexpr, поэтому я предполагаю, что он должен работать.

Ответы

Ответ 1

GCC прямо на этом.

Выражения, безусловно, constant-expression s *, поскольку они назначены переменной constexpr. Однако до С++ 14 существуют дополнительные ограничения на то, что разрешено для аргумента шаблона указателя.

С++ 14 черновик N4140 [temp.arg.nontype]

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

  • для нетипового шаблона-параметра интегрального или перечисляемого типа, преобразованное константное выражение (5.19) типа Шаблон-параметры; или
  • имя несимметричного шаблона; или
  • постоянное выражение (5.19), которое обозначает адрес полного объекта со статическим временем хранения и внешним или внутренним связь или функция с внешней или внутренней связью, включая шаблонов функций и шаблонов-шаблонов функций, но исключая нестатические члены класса, , выраженные (игнорируя круглые скобки), как &id-expression, где id-выражение - это имя объекта или кроме того, что и может быть опущено, если имя относится к функции или массива и должны быть опущены, если соответствующие шаблон-параметр является ссылкой; или
  • константное выражение, которое вычисляет значение нулевого указателя (4.10); или     постоянное выражение, которое вычисляет значение указателя нулевого элемента (4.11); или     указатель на элемент, выраженный как описано в 5.3.1; или     константное выражение типа std:: nullptr_t.

Для foo<dum_ptr>(), dum_ptr не выражается как &name, а для foo<&dum_ref>(), dum_ref не является именем объекта, это имя ссылки на объект, поэтому оба запрещены как аргументы шаблона.

Эти ограничения снимаются в С++ 17, чтобы разрешить любой constexpr, поэтому поэтому он работает там:

С++ 17 черновик N4606 - 14.3.2 Шаблон непиговых аргументов [temp.arg.nontype]

1 Аргумент шаблона для параметра шаблона, не относящегося к типу, должен быть преобразованное постоянное выражение (5.20) типа Шаблон-параметры. Для шаблона-шаблона, не относящегося к типу ссылки или тип указателя, значение константного выражения не должно ссылаться на (или для типа указателя, не должен быть адресом):

  • (1.1) подобъект (1.8),
  • (1.2) временный объект (12.2),
  • (1.3) строковый литерал (2.13.5),
  • (1.4) результат выражения типа (5.2.8) или
  • (1.5) предопределенная переменная __func__ (8.4.1).

Как обычно, clang дает лучшие сообщения об ошибках: https://godbolt.org/g/j0Q2bV


* (см. выражение константы адреса и ссылочное константное выражение)