C++ - 是否可以从模板中的成员函数类型中提取类和参数类型?

dav*_*idA 7 c++ templates member-function-pointers template-meta-programming

我想用模板化的类包装符合'void(ClassType :: Function)(ArgType)'类型的成员函数.稍后,我想将ClassType的实例传递给此模板的实例,并让它调用包装的方法:

class Foo {
 public:
  Foo() : f_(0.0) {}
  void set(double v) { f_ = v * 2.1; }
  double get() { return f_; }
 private:
  double f_;
};

template <typename ArgType, typename ClassType, void (ClassType::*Method)(ArgType)>
class Wrapper {
 public:
  explicit Wrapper(ClassType *cls) : cls_(cls) {}

  void do_something(ArgType value) {
    (cls_->*Method)(value);
  }

 private:
  ClassType *cls_;
};

#include <iostream>
int main(int argc, char ** argv) {
  Foo foo;
  Wrapper<double, Foo, &Foo::set> wrapper(&foo);

  wrapper.do_something(1.0);
  std::cout << foo.get() << std::endl;
  // outputs "2.1"
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在Wrapper <>的实例化中,"Foo"被指定了两次 - 这里看起来多余.

所以我想知道的是,是否可以避免使用模板参数ClassType.例如,如果可以从成员函数指针参数中暗示或提取它,那么就不需要在Wrapper <>的实例化中明确指定它.

以类似的方式,避免显式指定ArgType也是有用的,因为(也许)它可以从Foo :: set确定?

这在C++中是否可行?也许沿着这些(完全是幻想的)线条:

template <void (ClassType::*Method)(ArgType)>
class Wrapper2 {
 public:
  explicit Wrapper(Method::ClassType *cls) : cls_(cls) {}

  void do_something(Method::ArgType value) {
    (cls_->*Method)(value);
  }

 private:
  Method::ClassType *cls_;
};

// ...

int main() {
  Foo foo;
  Wrapper<&Foo::set> wrapper(&foo);
  // ...
}
Run Code Online (Sandbox Code Playgroud)

或者,也许还有另一个级别的模板魔法可以被调用,可以沿着这些方向做一些事情:

Wrapper<Magic<&Foo::set> > wrapper(&foo);
Run Code Online (Sandbox Code Playgroud)

我很想知道可能有哪些机制,如果有的话.

我使用C++ 03作为要求,而不是C++ 11,但也有兴趣知道C++ 11可能提供什么.

编辑:更多信息 - 我打算使用这个机制来包装~300个成员函数(都属于ClassType,或一组非常相似的类),但只有大约六个左右的签名需要考虑:

  • void(ClassType :: Function)(ArgType) - 其中ArgType是'浮动'
  • void(ClassType :: Function)(ArgType) - 其中ArgType是'integral'
  • void(ClassType :: Function)(bool)
  • void(ClassType :: Function)(IndexType,ArgType) - 上面三个带有额外的'index'参数

例如,成员函数是我在大型配置'集合'类中称为"属性"的"setter"函数(而不是上面的简单Foo):

class MyPropertyCollection {
 public:
  void set_oink(double value) { oink_ = value; }
  void set_bar(int value) { bar_ = value; }
  void set_squee(bool value) { squee_ = value; }
 private:
  double oink_;
  int bar_;
  bool squee_;
};

// elsewhere
WrapperCollection wrapper_collection;  // a simple set of wrapper objects, accessed by id
MyPropertyCollection property_collection;
wrapper_collection.add(PROPERTY_OINK_ID, new Wrapper<double, MyPropertySet, &MyPropertySet::set_oink>(&property_collection);
wrapper_collection.add(PROPERTY_BAR_ID, new Wrapper<int, MyPropertySet, &MyPropertySet::set_bar>(&property_collection);
wrapper_collection.add(PROPERTY_SQUEE_ID, new Wrapper<bool, MyPropertySet, &MyPropertySet::set_squee>(&property_collection);
// +300 more
Run Code Online (Sandbox Code Playgroud)

Omn*_*ous 1

::std::mem_fn这是+的一个糟糕的重新实现::std::bind,它是 C++11 结构。您可以使用以下方法来执行此操作:

\n\n
#include <functional>\n\nint main() {\n   Foo foo;\n   auto wrapper = ::std::bind(::std::mem_fn(&Foo::set), ::std::ref(foo), _1);\n   wrapper(5); // Calls foo.set(5)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,当然,您需要 C++03 解决方案。使用 Boost 可以在 C++03 中实现这一点。我也相信在 C++03 和 TR1 中类似的事情是可能的。您可以通过查看是否#include\xc2\xa0<tr1/functional>有效来判断您是否拥有该功能。如果您有 TR1,它们会显示在::std::tr1名称空间中。

\n\n

现在,有一种方式却并非如此。您已将函数指针本身作为类类型签名的一部分。这是一件奇怪的事情,但正如您所知,这肯定是可能的。不过,能够在编译时确定ClassType和值是很棘手的。ArgType您可以通过模板函数参数匹配来做到这一点,但没有用,因为 C++03 没有auto.

\n