函数指针作为模板参数

12

在下面的C++代码中,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的地址作为模板参数传递时它可以工作,但是当我传递constexpr func_ptr时,即使在编译时它确切地保留了foo的地址,代码也无法编译。有人能解释一下吗?

编辑:我的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.

2
使用gcc8和clang6没有任何问题。 - Baum mit Augen
2
在这里(C++17),按预期工作。 - Jarod42
2
在C++14中出现了错误演示 - Jarod42
我记得自从C++11以来,指向全局变量的指针与constexpr字面值作为模板参数的变化之一。但我不记得这是否是其中之一,也不知道如何找到它。 - Yakk - Adam Nevraumont
和Jarod42有相同的经验。C++17可以工作;C++14会报错。 - Eljay
非常感谢!我以为我误解了什么。 - phinz
1个回答

11
https://en.cppreference.com/w/cpp/language/template_parameters 中指出:
对于函数指针,有效的参数是具有链接的函数指针或计算结果为null指针值的常量表达式。(直到C++17)。
由于constexpr fun_ptr_type func_ptr=&foo在编译时不会被评估为nullptr值,因此如果您使用-std=c++14-std=c++11运行它,则失败。
然而,C++17对函数指针非类型模板参数没有这样的要求。它说:
用于非类型模板参数的模板参数可以是转换为与模板参数类型相同的任何转换常量表达式。(自C++17以来)
(上述内容有一些例外情况,但都不适用于函数指针)。
因此,您提供的代码可以完美地使用-std=c++17选项运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接