3

我有这个功能:

template <typename T, void (T::*pf)()>
void call(T& t)
{
    (t.*pf)();
}

如果我有foo一个带有适当签名的方法的类(比如bar),我可以这样调用它call<foo, &foo::bar>();,这很好。但是,如果barconstgcc 和 msvc 在这样调用时很乐意编译它call<const foo, &foo::bar>()。Clang 抱怨第二个模板参数无效。当我放入const模板参数(void (T::*pf)() const)时,所有树都会编译它。

现在,这不是一个大问题,但是如果我不必const在模板参数中编写这个可恶的代码,我的代码就会变得更加清晰。

所以问题基本上是:标准对此有何评论?这是一个clang错误还是gcc和msvc只是让它滑动,因为它们很酷?

PS这是一个完整的repro程序的链接:http://codepad.org/wDBdGvSN

4

1 回答 1

2

const方法的特性是它的“签名”的一部分。因此,定义和使用指向成员的指针的正确方法是:

R (Obj::*)(Args)       // for non-const member
R (Obj::*)(Args) const // for const member

请注意,const可以在非常量对象上调用成员,而R (const Obj::*)(Args).

解决此问题的一种方法是通过定义“调用包装器”来抽象此类函数指针:

template<typename O, void (O::* f)()>
struct NonConstFunc
{
    static void call(O* o)
    {
        (o->*f)();
    }
};

template<typename O, void (O::* f)() const>
struct ConstFunc
{
    static void call(O* o)
    {
        (o->*f)();
    }
};

然后,您可以通过以下方式使用它(此处进行抽象):

template<typename Obj, typename Function>
void call(Obj* o)
{
    Function::call(o);
}

这里有一个活生生的例子

这只是主要思想。您可以通过自动检测方法是否存在来扩展它const,而无需更改用户代码。

于 2013-03-08T12:41:31.717 回答