2014-10-28 138 views
2

我对C++和模板都很陌生,我尝试修改一个可用于使委员从成员功能。“变量”不是类型'指向成员函数'的有效模板参数

我改变了这种代码(编译):

Delegate<void, int, int>* cheaty(void (Renderer::*method)(int, int), Renderer* obj) 
    { 
     auto maker = DelegateMaker<Renderer, void, int, int>(); 
     return maker.BindPointer<&Renderer::MoveCamera>(obj); 
    } 

要这样:

Delegate<void, int, int>* cheaty(void (Renderer::*method)(int, int), Renderer* obj) 
    { 
     auto maker = DelegateMaker<Renderer, void, int, int>(); 
     return maker.BindPointer<method>(obj); 
    } 

这并不编译并提供了以下错误:error: 'method' is not a valid template argument for type 'void (Engine::Renderer::*)(int, int)'。由于C++模板的局限性,我试图实现的目标根本不可能实现吗?还是我错过了一些非常明显的东西?

编辑: 理想情况下,我想有是这样的功能:

template<typename T, typename return_type, typename... params> 
    Delegate<return_type, params...>* make_delegate_pointer(return_type (T::*name)(params...), T* obj) 
    { 
     DelegateMaker<T, return_type, params...> maker = DelegateMaker<T, return_type, params...>(); 
     return maker.BindPointer<name>(obj); 
    } 

然后调用它像这样auto delegate = Delegates::make_delegate_pointer(&Class::Method, &classInstance);只是,我一直运行到的问题是,似乎有事实在调用BindPointer这样的maker.BindPointer<&Class::Method>(classInstance);并将其称为maker.BindPointer<return_type (Class::*)(arguments...)>(classInstance);之间有些区别。

但是,return_type (T::*)(params...) memberFuncPointer = &Class::Method编译得很好。这意味着在逻辑上,maker.BindPointer<return_type (T::*)(params...)>(classInstance)也应该编译(或者对其进行一些修改,这就是为什么我试图用method作为void (Renderer::*method)(int, int)的原始问题)。但事实并非如此。

回答

3

在编译时必须知道模板参数值,但是method值直到运行时才会知道,因为cheaty()不知道将哪个方法Renderer传递给它。因此编译器错误。您将不得不更改BindPointer以不再使用模板。另外,DelegateMaker已经知道了方法签名,因此您不需要将该信息作为BindPointer()中的模板参数进行复制,让它继承DelegateMaker中的值。

例如(未经测试,可能需要一些调整,但你应该得到的总体思路):

template <typename ReturnType, typename... ParamTypes> 
class Delegate 
{ 
public: 
    virtual ReturnType Invoke(ParamTypes... params) = 0; 
}; 

template <typename ClassType, typename ReturnType, typename... ParamTypes> 
class DelegateMaker 
{ 
public: 
    typedef ReturnType (ClassType::*MethodType)(ParamTypes... params); 

private: 
    class DelegateImpl : public Delegate<ReturnType, ParamTypes...> 
    { 
    private: 
     ClassType* _obj; 
     MethodType _method; 

    public: 
     DelegateImpl(ClassType *obj, MethodType method) 
      : _obj(obj), _method(method) 
     { 
     } 

     virtual ReturnType Invoke(ParamTypes... params) 
     { 
      return (_obj.*_method)(params); 
     } 
    }; 

public: 
    Delegate<ReturnType, ParamTypes...>* BindPointer(MethodType method, ClassType *obj) 
    { 
     return new DelegateImpl(obj, method); 
    } 
}; 

template <typename ClassType, typename ReturnType, typename... ParamTypes> 
Delegate<ReturnType, ParamTypes...>* make_delegate_pointer(
    DelegateMaker<ClassType, ReturnType, ParamTypes...>::MethodType method, 
    ClassType* obj) 
{ 
    DelegateMaker<ClassType, ReturnType, ParamTypes...> maker; 
    return maker.BindPointer(method, obj); 
} 

auto delegate = Delegates::make_delegate_pointer(&Class::Method, &classInstance); 
+0

请参阅编辑我的问题。虽然我知道这些值必须在编译时知道,但我不明白编译器无法解决这个问题。因为它是在编译时知道的,以方法参数的形式。而且,渲染器传递给它的方法到底有没有关系?不应该该方法适合模式'void(Renderer ::)(int,int)'? – TheDutchDevil 2014-10-29 09:39:24

+0

不,编译时'方法'的值*不知道,那就是问题所在。它是一个* runtime *值。想象一下,如果你有2 +''Renderer'方法传递给'cheaty()',例如:'cheaty(&Renderer :: DoThis,SomeRendererObject); cheaty(&Renderer :: DoThat,SomeRendererObject);'。 'cheaty()'本身不是一个模板函数,所以编译器不能根据编译时传递的方法创建不同的版本。所有'cheaty()'都是一对内存地址,不能在编译时确定,仅在运行时才能确定。 – 2014-10-29 16:08:06

+0

好的,但不是应该使用'method'的类型,而不是'method'的值?在'cheaty'的情况下它是'void(Renderer :: *)(int,int)。因为如果'Renderer :: DoThis'和'Renderer :: DoThat'具有相同的返回类型,参数并且是'Renderer'类的成员函数。 'Renderer :: DoThis'和'Renderer :: DoThat'具有相同的类型('void(Renderer :: *)(int,int)')。哪些用于模板,对不对? – TheDutchDevil 2014-10-29 22:53:48

相关问题