Указатели функций как аргументы шаблона

В следующем коде C++ строка bar<func_ptr>();//does not work bar<func_ptr>();//does not work вызывает ошибку компиляции:

#include <iostream>

using namespace std;

void foo(){
    cout<<"Hello world";
};

template<void(*func)()> 
void bar(){
    (*func)();
}

int main() {
    using fun_ptr_type= void(*)();
    constexpr fun_ptr_type func_ptr=&foo;

    bar<&foo>();     //works
    bar<func_ptr>(); //does not work
    return 0;
}

Результат работы g++ таков:

src/main.cpp: In function ‘int main():
src/main.cpp:19:16: error: no matching function for call to ‘bar()
  bar<func_ptr>(); //does not work
                ^
src/main.cpp:10:6: note: candidate: template<void (* func)()> void bar()
 void bar(){
      ^~~
src/main.cpp:10:6: note:   template argument deduction/substitution failed:
src/main.cpp:19:16: error: ‘(fun_ptr_type)func_ptr is not a valid template argument for ty
pe ‘void (*)()
  bar<func_ptr>(); //does not work
                ^
src/main.cpp:19:16: error: it must be the address of a function with external linkage

Я не понимаю, почему это работает, когда я напрямую передаю адрес foo как аргумент шаблона, но когда я передаю func_ptr, код больше не компилируется, даже если он содержит именно тот адрес foo во время компиляции. Может кто-то объяснить это мне?

EDIT: Моя версия g++

$ g++ --version
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ответы

Ответ 1

Из https://en.cppreference.com/w/cpp/language/template_parameters говорится:

Для указателей на функции действительными аргументами являются указатели на функции с привязкой (или константные выражения, которые оценивают значения нулевого указателя). (до С++ 17).

Поскольку constexpr fun_ptr_type func_ptr=&foo не оценивает значение nullptr во время компиляции, он терпит неудачу, если вы запустите его с помощью -std=c++14 или -std=c++11.

Однако С++ 17 не налагает такого требования на параметры шаблона не указателя функции. В нем говорится:

Аргумент шаблона, который может использоваться с параметром шаблона непигового типа, может быть любым преобразованным константным выражением типа параметра шаблона. (поскольку С++ 17)

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

Таким образом, код, который вы предоставляете, отлично работает с -std=c++17.