我正在尝试编写自己的委托系统作为boost :: functions的替代品,因为后者执行了大量的堆分配,我认为这些分配是有问题的。
我已经把它写成了替代品(简化后,实际的东西使用了池化内存和新位置,但这很简单,可以重现错误):
template<class A, class B>
struct DelegateFunctor : public MyFunctor {
DelegateFunctor(void (*fptr)(A, B), A arg1, B arg2) : fp(fptr), a1(arg1), a2(arg2) {}
virtual void operator()() { fp(a1, a2); }
void (*fp)(A, B); // Stores the function pointer.
const A a1; const B a2; // Stores the arguments.
};
和这个助手功能:
template<class A, class B>
MyFunctor* makeFunctor(void (*f)(A,B), A arg1, B arg2) {
return new DelegateFunctor<A,B>(f, arg1, arg2);
}
奇怪的事情发生在这里:
void bar1(int a, int b) {
// do something
}
void bar2(int& a, const int& b) {
// do domething
}
int main() {
int a = 0;
int b = 1;
// A: Desired syntax and compiles.
MyFunctor* df1 = makeFunctor(&bar1, 1, 2);
// B: Desired syntax but does not compile:
MyFunctor* df2 = makeFunctor(&bar2, a, b);
// C: Not even this:
MyFunctor* df3 = makeFunctor(&bar2, (int&)a, (const int&)b);
// D: Compiles but I have to specify the whole damn thing:
MyFunctor* df4 = makeFunctor<int&, const int&>(&bar2, a, b);
}
我为版本C获得的编译器错误(B类似)是:
error: no matching function for call to ‘makeFunctor(void (*)(int&, const int&), int&, const int&)’
这很奇怪,因为编译器在其错误消息中实际上正确地推断出类型。
有什么方法可以让B版进行编译吗? boost :: bind如何解决这个限制?
我正在使用GCC 4.2.1。请不要使用C ++ 11解决方案。
答案 0 :(得分:5)
参数扣除剥离参考文献。通过匹配A
的函数指针签名,我们希望获得int &
,但是通过匹配实际参数,我们需要int
,因此扣除失败。
一种解决方案是使非推导出第二种类型,如下所示:
#include <type_traits>
template <typename R, typename A, typename B>
R do_it(R (*func)(A, B),
typename std::common_type<A>::type a, // not deduced
typename std::common_type<B>::type b) // not deduced
{
return func(a, b);
}
现在A
和B
完全由函数指针的签名决定:
int foo(int &, int const &);
int main()
{
int a = 0, b = 0;
return do_it(foo, a, b); // deduces A = int &, B = int const &
}
(请注意,std::common_type<T>::type
是“身份类型”的推荐习惯用法,其唯一目的是从参数推断中删除模板参数。以前称之为identity<T>::type
或alias<T>
,但标准库特征std::common_type
可以很好地实现此目的。)
答案 1 :(得分:2)
使用值参数推断模板参数时,您将只获得值类型。也就是说,当你使用像
这样的功能模板时template <typename T>
void f(T) {
}
类型T
始终是非引用类型。现在,当您尝试传递函数指针和值时,如果函数不采用值类型,则编译器无法使推导的类型保持一致:
template <typename T>
void f(void (*)(T), T) {}
void f0(int);
void f1(int const&);
int main() {
f(&f0, 0); // OK
f(&f1, 0); // ERROR
}
解决此问题的一种方法是适当地重载相应的功能模板。如果您将以下函数添加到混合中,则上述示例再次起作用:
template <typename T>
void f(void (*)(T const&), T const&) {}
显然,这很快成为维护的噩梦,可能不你想做什么。另一种方法是为相应的参数使用不同的模板参数:
template <typename T, typename S>
void f(void (*)(T), S) {}
虽然这样做有效,但这会产生直接影响,你不一定要匹配第二个参数你真正想要匹配的类型:即使你想获得一个引用类型,它也会是一个值类型(个人而言,我怀疑你这样做,但这是一个不同的问题)。如果您不希望发生这种情况,可以阻止编译器表单尝试推导某些参数的参数。例如:
template <typename T>
struct helper {
typedef T type;
};
template <typename T>
void f(void (*)(T), typename helper<T>::type) {}
虽然上面的例子只是使用一个模板参数来演示手头的问题,但我确信这也适用于更多的模板参数。这是否是Boost,我既不知道也不关心。