【问题标题】:C++ passing method pointer as template argumentC++ 将方法指针作为模板参数传递
【发布时间】:2010-12-08 13:28:34
【问题描述】:

我有一个这样的调用函数:

template<typename T, void (T::*method)()>
void CallMethod(T *object){
    (object->*method)(args);
}

虽然这很有效:

void (*function)(A *) = &CallMethod<A, &A::method>;

此代码无法编译,第二行出现错误:

void (A::*method)() = &A::method;
void (*function)(A *) = &CallMethod<A, method>;

有什么办法可以解决吗?我需要 CallMethod 模板来获取一个指向存储在变量中的方法的常量指针。

【问题讨论】:

  • 这是无法编译的实际代码吗?这里的“方法”是什么?您正在将一个名为方法的成员函数分配给一个也称为方法的变量,并且该成员函数变量被声明为错误..
  • 你为什么不使用 std::tr1::function/boost::function 和绑定呢?
  • 'method'只是A的一些方法,不是实际代码,我尽量简化了。
  • boost:function/bind 如果他真正想要的是一个带有 A* 参数的函数,它将不起作用。我认为他正在尝试动态创建一个。
  • @CashCow:您可以将bind 的成员函数指向对象,从返回的function 中获取函数指针,然后使用它...

标签: c++ templates pointers methods


【解决方案1】:

所有模板参数必须在编译时已知。所以如果method 真的是一个变量而不是一个特定的函数,那么就没有办法做你想做的事了。

相反,您可以创建一个template struct,它有一个指向成员函数的指针作为成员,并实现了一个类似于CallMethod&lt;A, method&gt;operator()

【讨论】:

  • 这不是一个选项。结果我需要一个函数指针,因为我将对象传递给纯 C。
  • 在这种情况下,传递具有足够信息的东西作为对象指针。使用A* 指针作为唯一的函数参数不足以确定要调用的方法。请参阅@CashCow 的回答。
【解决方案2】:

此时我已经确定您有一个接受函数和指针的 API,并且您需要提供这样的 API。我假设您必须始终为其提供一个 A* 指针?

如果它是一个非常通用的回调,但必须是一个函数(不能是 boost::function)并且必须是一个指针(可能是 void*),那么你需要一个有点像这样的函数:

struct GenericHolder
{
   boost::function0<void> func;
};

void GenericCallback( void * p )
{
   GenericHolder * holder = static_cast< GenericHolder * >(p);
   holder->func();
   delete holder;
}

在这种情况下,我在调用时调用 delete,所以我假设我们在调用时调用 new,即当你建立你的调用指针时。当然,您传递的指针可能不会在第一次调用时被删除,因此请适当管理生命周期。

如果你控制着“另一边”,那么不要故意这样做,而是让那一边持有 boost::function 并调用它。

内存管理仍然是您需要注意的问题。例如,当您调用 boost::bind 时,它会在幕后为您包装一个结构体。如果这些是你用 new 分配的指针,你需要在某个时候删除它们。如果它们是参考,它们必须在调用点仍然有效。指向局部变量的指针也可能是一个问题。 shared_ptrs 是理想的,当然。如果你经常使用这个概念,那么对于 boost::bind 错误的错误跟踪是非常非常难以找到的。

【讨论】:

  • 谢谢,这会奏效。这不正是我想要的。我想在编译时生成函数(方法包装器)。您的方法在运行时完成了我认为不太优雅的工作,但它仍然可以工作。
【解决方案3】:

您似乎正在尝试通过模板实现接口。有可能,但是有一个带有虚函数的基类,并使用基类指针通过简单的函数调用来访问虚函数不是更好吗?

标题:

class AbstractBase
{
public:
    virtual void func() = 0;
}
class SubClass : public AbstractBase
{
public:
    void func();
}

源文件:

void SubClass::func()
{
    std::cout << "Virtual function called.\n";
}

示例用法:

int main()
{
    AbstractBase* base;
    base = new SubClass();
    base->func(); // virtual function call through base pointer will use SubClass's implementation
    return 0;
}

您可以存储一个指针向量(如果您使用 boost 或 C++0x,则为智能指针),您可以循环并使用它来执行各种依赖于子类的事情。

或者,使用 boost::functionstd::function (C++0x),这是一个包含相关成员函数的对象,并将其作为模板参数传递,使用对象实例作为第一个参数。这归结为上述解决方法。

更新:看到您需要一个普通的 C 函数指针,有一些技巧可能会影响 c++0x 中的运行时性能或提升:bindfunction::target 等等。 ..

您将需要this to get a function pointer out of a std/boost::functionthis to bind the first argument to an objectbind 在运行时起作用,所以在这种情况下,模板可能会更好......

【讨论】:

  • 我需要一个普通的函数指针而不是方法调用,所以我可以将它传递给 C。
  • function::target 在这里不起作用。 [此答案][1] 对您的链接问题的回答与@CashCow 对这个问题的回答基本相同。 [1]stackoverflow.com/questions/282372/…
【解决方案4】:

首先,请创建类型定义http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.5

第二,

void (*function)(A *) = &CallMethod<A, method>;

在这种情况下,方法是一个变量,变量不能是模板参数。我还没有测试过,但也许你可以试试......

void (*function)(A *) = &CallMethod<A, void (A::*method)()>;

【讨论】:

  • 不过,您不能创建模板化的 typedef。
  • 不,但他可以做 typedef void (A::*AMethod)();然后我们可以将该类型称为 AMethod
  • 模板可以通过常量变量来实例化。问题是如何为方法指针实现这一点。
  • 首先,您没有定义常量变量。二、我的第二行不行吗?
  • @Andrew 我不知道你提议的线路应该是什么意思。最好再次删除它,它根本没有意义。就我而言,在“我尚未测试...”部分之前,您的回答很好。然后它变成了guess answer
【解决方案5】:

如果两个方法具有完全相同的签名怎么办?

模板无法区分它(它选择类型,而不是值),因此生成的相同模板函数将调用第一个方法或第二个方法,你敢赌它吗?

您可以在编译时使用一些技巧来创建不同的类型,使用 __LINE__ 宏,但它涉及将宏与当前代码混合,这将很难遵循。 这段代码:

template<typename T, void (T::*method)(), int>
void CallMethod(T *) { ... }
#define MakeCallMethod(X, Y) &CallMethod<X, X::&Y, __LINE__>

// This will work (it does not without the __LINE__ trick, ptr1 == ptr2 in that case)
typedef void (*ptrFunc)(A *);
ptrFunc ptr1 = MakeCallMethod(A, method);
ptrFunc ptr2 = MakeCallMethod(A, otherMethodWithSameSignature);

Assert(ptr1 != ptr2);

但是,您将无法将指向方法的指针保存在变量中并从中创建“自动包装器”。现在是运行时,您需要一个运行时解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-01
    • 2020-10-26
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 2017-11-26
    相关资源
    最近更新 更多