Static constexpr pointer-to-function, разница между компиляторами

Отвечая на этот вопрос, я попробовал следующий код с gcc (скомпилированный код) и clang (код отклонен):

typedef long (*func)(int);

long function(int) { return 42; }

struct Test
{
    static constexpr func f = &function;
};

template<func c>
struct Call
{
    static void f()
    {
        c(0);
    }
};

int main()
{
    Call<Test::f>::f();
}

Я не уверен, какой компилятор прав, хотя я думаю, что инициализация constexpr Test::f в порядке. Выходы с ошибкой:

error: non-type template argument for template parameter of pointer type 'func'
       (aka 'long (*)(int)') must have its address taken
  • Какой компилятор прав?
  • Если clang прав, почему и что означает эта ошибка?

EDIT: для "почему", см. вопрос DyP.

Ответ 1

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

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

[...]

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

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

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


Изменить: расширенный ответ из-за последующего вопроса (комментария) Synxis

constexpr func = &function;

^ это хорошо сформировано; вы можете использовать адрес функции для инициализации объекта constexpr. Проблема в том, что он явно запретил использовать указатели как аргументы шаблона, отличные от формы &identifier:

using My_Call     = Call < &function >;  // fine

constexpr func mypointer = &function;    // fine
using My_Ind_Call = Call < func >;       // forbidden, argument not of form `&id`