指向带有继承的重载成员函数的指针的非类型模板参数

时间:2014-06-25 09:03:46

标签: c++ templates pointer-to-member

背景

请考虑以下代码:

struct A { // a class we want to hide from the client code
  int f();
  int f(char);
  int g();
};

struct B : A {}; // the interface class

// client code:
//
// It's natural to assume a member function of T should have
// a type of int (T::*)(), right?
template <typename T, int (T::*)()> struct F {};

// compiles, of course
F<A, &A::g> {};

// doesn't compile, because &B::g is not of type `int (B::*)()`, and no
// conversion is allowed here
F<B, &B::g> {};

// compiles, because &B::g is actually &A::g
//
// but this is not I want because the class hierarchy may be deep and it's
// not easy to know what A is.
F<A, &B::g> {};

片段struct<B, &B::g> {}无法编译,因为

  1. &B::g的类型为int (A::*)(),而不是int (B::*)();
  2. 虽然从int (A::*)()int (B::*)()的隐式转换是合法且可行的,但在执行模板参数替换点到成员函数模板时,C ++标准禁止(!!)任何转换参数。 (严格来说,允许转换为nullptr_t。)
  3. 因此,F<B, &B::g>无法匹配F的确切定义,并且无法编译。这很难过,因为class A可能是我们不想打扰的实施细节。

    解决方法

    天真的黑客将是

    template <typename T, T val> struct G {};
    
    // at least compiles, and don't have to be aware of A
    G<decltype(&B::g), &B::g> {};
    

    到目前为止一切顺利。

    问题

    上述hack不适用于重载的类方法。通常,重载可以通过static_cast来解决,但是这需要我们知道&amp; B :: f的确切类型 - 一个很好的鸡蛋和鸡蛋情况。

    // won't compile since &B::f is ambiguous
    G<decltype(&B::f), &B::f> {};
    
    // won't compile just like F<B, &B::g>
    G<decltype(static_cast<int(B::*)()>(&B::f)), &B::f> {};
    
    // compiles, but require the knowledge of the type of &B::f
    G<decltype(static_cast<int(A::*)()>(&B::f)), &B::f> {};
    

    G<decltype(static_cast<int(A::*)()>(&B::f)), &B::f> {};这样的东西太糟糕了。

    总结一下,问题是如何正确选择特定的重载,并避免在A实际为&B::f时提及基类&A::f

    有什么想法吗?

1 个答案:

答案 0 :(得分:4)

我找到了符合我要求的解决方法。希望它对有需要的人有所帮助。我被困了几天......

这个想法是使用函数模板来匹配特定的重载,然后将正确的类型传递给G。间接层总能拯救世界。

template <typename T>
auto forward_pmf_with_signature( int(T::*pmf)() ) -> decltype(pmf);

G<decltype(forward_pmf_with_signature(&B::f)), &B::f> {}; // works
在没有G意识的情况下使用

A并选择正确的重载,很酷。

现在新问题是我发现G<decltype(forward_pmf_with_signature(&B::f)), &B::f>是多余的并且容易出错。使用宏USE_G(&B::f)来简化代码是微不足道的,但在我看来,以语义方式进行简化并不容易,甚至可能。