4

我正在尝试使用具有默认参数的函数作为函数指针模板参数:

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 (*)()”</p>

是否有特定的语法可以工作?还是特定的语言限制?否则,替代方法是使用两个单独的函数而不是默认参数:

void foo1(int a) {}
void foo1() { foo1(0); }

更新 我知道签名是不同的,但我想知道是否有一种方法可以方便地进行这项工作,而无需使用默认参数修改所有函数?

4

5 回答 5

3

的签名foo1void(int),不是void()。这就是为什么它不能转换为void(*)().

您将默认参数与重载混淆了。

于 2012-11-16T23:00:17.130 回答
2

默认参数值不是函数类型的一部分。您不能将foo1其用作不带参数的函数,因为它确实只带一个参数。如果您不提及它,该论点就会为您填充,但它仍然存在。

您涉及调度功能的解决方法听起来像是一个很好的解决方案。如果您非常需要它,它甚至可以被模板化。

于 2012-11-16T23:00:09.843 回答
2

我很确定函数指针具有扩展所有默认参数的函数签名,并且函数指针无法转换为具有不同签名的函数指针。但是,在标准中找到这一点是另一回事……

我认为标准中的相关条款是 8.3.5 [dcl.fct] 第 6 段:

...返回类型、参数类型列表、引用限定符和 cv-qualifier-seq,但不是默认参数 (8.3.6) 或异常规范 (15.4),是函数的一部分类型。[注:函数类型在函数指针、函数引用和成员函数指针的赋值和初始化过程中检查。——尾注]

请注意,默认参数= value根据 8.3.6 [dcl.fct.default] 第 1 段的形式:

如果在参数声明中指定了初始化子句,则此初始化子句用作默认参数。...

于 2012-11-16T23:01:12.697 回答
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:每次调用函数时都会评估默认参数。

默认参数的存在不会改变函数的签名。例如,您不能使用带有默认参数的单参数函数来覆盖无参数的虚拟成员函数。

于 2012-11-16T23:05:24.157 回答
1

它不会编译,因为foo1有签名:

void foo1(int a);

您试图将其插入指向:

void F()

函数签名不匹配。具有默认参数的事实foo1不会改变函数的签名(它仍然可以接受int)。

更通用的解决方案

我会说忘记模板,它们只是在这里限制你。

就个人而言,我解决回调问题的方法是使用带有参数绑定的函数对象。它可以使用boost::function库来完成,并使用boost::bind(或and )绑定默认参数。std::bind1ststd::bind2nd

这些 boost 库也内置于新的 C++11 标准中,asstd::functionstd::bind.

值得一看,因为它可以让你做一些非常好的事情,比如为函数提供默认参数,或者使用类成员函数作为回调。

我链接到的网站都有很多示例代码,boost 链接有教程。

于 2012-11-16T23:04:45.720 回答