我正在尝试使用带有默认参数的函数作为函数指针模板参数:
template <void (*F)()>
class A {};
void foo1(int a = 0) {}
void foo2() {}
int main()
{
//A<foo1> a1; <-- doesn't work
A<foo2> a2;
}
编译错误是:
main.cpp:7:7:错误:无法将模板参数'foo1'转换为'void(*)()'
这是否有特定的语法?还是特定的语言限制?否则,替代方法是使用两个单独的函数而不是默认参数:
void foo1(int a) {}
void foo1() { foo1(0); }
更新 我知道签名是不同的,但我想知道是否有一种方法可以方便地使这项工作,而无需用默认参数修改所有函数?
答案 0 :(得分:3)
foo1
的签名是void(int)
,而不是void()
。这就是为什么它不能转换为void(*)()
。
您将默认参数与重载混淆。
答案 1 :(得分:2)
默认参数值不是函数类型的一部分。您不能将foo1
用作不带参数的函数,因为它确实需要一个参数。如果你没有提到它,那么这个论点会被你填好,但它仍然存在。
涉及调度功能的解决方法听起来像是一个很好的解决方案。如果你需要它,它甚至可能会被模板化。
答案 2 :(得分:2)
我很确定函数指针具有函数的签名,所有默认参数都已扩展,函数指针无法转换为具有不同签名的函数指针。在标准中找到这个是另一回事,但是......
我认为标准中的相关条款是8.3.5 [dcl.fct]第6段:
...返回类型,参数类型列表,ref-qualifier和cv-qualifier-seq,但不是默认参数(8.3.6)或异常规范(15.4),是功能类型。 [注意:在指向函数的指针和函数的引用以及指向成员函数的指针的赋值和初始化期间,将检查函数类型。 - 后注]
请注意,根据8.3.6 [dcl.fct.default]第1段,默认参数是= value
形式的人:
如果在参数声明中指定了initializer子句,则此初始化子句用作默认参数。 ...
答案 3 :(得分:2)
根据C ++标准的第8.3.6节,
如果在参数声明中指定了表达式,则此表达式将用作默认参数。默认参数将用于缺少尾随参数的调用中。
由于A<foo1>
不调用该函数,因此忽略默认参数。实际上,除了函数的调用之外,它们在所有上下文中都被忽略,例如
typedef void (*FFF)();
FFF x = foo1;
将无法编译,并生成尝试使用foo1
作为模板参数时获得的相同消息:
error: invalid conversion from ‘void (*)(int)’ to ‘void (*)()’
这是有道理的,因为评估默认参数是调用中的一个单独步骤:
8.3.6.9:每次调用函数时都会计算默认参数。
默认参数的存在不会改变函数的签名。例如,您不能使用带有默认参数的单参数函数来覆盖无参数的虚拟成员函数。
答案 4 :(得分:1)
它不会编译,因为foo1
有签名:
void foo1(int a);
你试图坚持指向:
void F()
功能签名不匹配。 foo1
具有默认参数的事实不会更改函数的签名(它仍然可以包含int
)。
我会忘记模板,他们只是限制你。
就个人而言,我解决回调问题的方法是使用函数对象和参数绑定。可以使用boost::function库完成此操作,并使用boost::bind(或std::bind1st
和std::bind2nd
)绑定默认参数。
这些增强库也内置于新的C ++ 11标准,std::function
和std::bind
。
值得一看这个,因为它让你做一些非常好的事情,比如为函数提供默认参数,或者使用类成员函数作为回调。
我链接到的网站都有很多示例代码,而boost链接都有教程。