【问题标题】:local variable as non-type template argument局部变量作为非类型模板参数
【发布时间】:2011-06-29 20:34:01
【问题描述】:

我想做如下的事情:

Example(&Class::MemberFunction, this));

//...

template<class T_CLASS>
inline static void Example(void (T_CLASS::*MemberFunctionPointer)(), T_CLASS* InstancePointer)
{
  SomeClass<T_CLASS>::Bind<MemberFunctionPointer>(InstancePointer);
}

但我收到错误:*模板参数 'T_MEMBER_FUNCTION' : 'MemberFunctionPointer' : a local variable cannot be used as a non-type argument*

这个问题有什么解决办法吗?我想提供一种更简单的方法来调用“绑定”

谢谢,米尔科

//编辑:

我希望 MemberFunctionPointer 成为非类型模板参数,因为在“绑定”中我再次需要它作为模板参数。 正如您在答案中所写,在我的情况下 MemberFunctionPointer 是一个变量,它的值在编译时是未知的。但是 MemberFunctionPointer 总是指向同一个函数。有没有办法让它保持不变,以便编译器在编译时知道它?

【问题讨论】:

标签: c++ templates local non-type


【解决方案1】:

模板参数可以是两种类型:类型和编译时常量表达式。函数参数的内容不是编译时可确定的值。因此,编译器无法实例化基于它的模板。

记住:模板是一个类型。并且类型必须在编译时确定。

您可能应该将成员指针作为参数传递给Bind 函数。

【讨论】:

    【解决方案2】:

    我不太确定你想要达到什么目的?

    如果MemberFunctionPointer 是一个变量,那么它的值在编译时是未知的,例如,它可能取决于某些用户行为——那么它就不能用作模板参数。

    另一方面,如果MemberFunctionPointer 可以在编译时实际推导出来,则应将其作为模板参数而不是函数参数传递。考虑以下示例:

    (第一种情况使用Bindcall;第二种情况使用StaticBindcallStatic

    #include <stdio.h>
    
    class X {
      public:
        int x;
        void foo() {printf("foo\n");}
        void bar() {printf("bar\n");}
    };
    
    template <typename T>
    class SomeClass {
      public:
        static void Bind(void (T::*MemberFunctionPointer)(), T *obj) {
          (obj->*MemberFunctionPointer)();
        }
        template <void (T::*MemberFunctionPointer)()>
        static void StaticBind(T *obj) {
          (obj->*MemberFunctionPointer)();
        }
    };
    
    template <class C>
    static inline void call(void (C::*MemberFunctionPointer)(), C *obj) {
      SomeClass<C>::Bind(MemberFunctionPointer,obj);
    }
    
    template <class C, void (C::*MemberFunctionPointer)()>
    static inline void callStatic(C *obj) {
      SomeClass<C>::template StaticBind<MemberFunctionPointer>(obj);
    }
    
    int main() {
      X obj;
      call<X>(&X::foo,&obj);
      callStatic<X,&X::bar>(&obj);
      return 0;
    }
    

    【讨论】:

    • 我希望它在编译时被推断出来,但我不知道如何。所以第二种情况更适合......只是我尝试提供一个不接受任何模板参数的方法“调用”。
    • 这样的话,其实可以用第一种方法,加内联所有函数。但是,如示例中所示,Bind 函数必须通过参数而不是模板参数来获取成员函数指针。启用优化后,它应该归结为一个常数,并且您应该不会遇到运行时开销。函数call的模板&lt;C&gt;参数其实可以从传入的参数推导出来,所以调用中其实可以跳过。为了清楚起见,我把它放在那里。
    【解决方案3】:

    模板参数必须在编译时知道。作为函数参数的指针变量的内容取决于调用该函数的方式。这在编译时是未知的!

    如果你在编译时已经知道这个指针,你可以把函数指针运行时参数变成模板参数:

    template<class T_CLASS, void(T_CLASS::*MemFunPtr)()>
    void Example(T_CLASS* InstancePointer) {...}
    

    这里,MemFunPtr 是在编译时已知的模板参数,因此可以重新用作另一个函数或类模板的模板参数...

    【讨论】:

    • 谢谢,这就是我想做的。据我了解,您不可能将 MemFunPtr 作为普通参数传递并避免将其作为模板参数传递给示例,对吧?
    【解决方案4】:

    MemberFunctionPointer 是一个变量而不是 类型(或编译时常量),因此不能使用,您需要的是该函数的真正签名,这样的东西可能会更好。 .

    template<typename T_FUNC_PTR, class T_CLASS>
    inline static void Example(T_FUNC_PTR fPtr, T_CLASS* InstancePointer)
    {
      SomeClass<T_CLASS>::Bind<T_FUNC_PTR>(fPtr, InstancePointer);
    }
    

    即让编译器推断出函数指针的类型(注意:您也必须将指针传播到函数),以调用

    Example(&foo::bar, foo_inst);
    

    这是未经测试的,我想不通,所以语法可能有点不对...

    编辑:这里有一个更简单的例子来演示这个概念:

    #include <iostream>
    struct foo
    {
      void bar() { std::cout << "foo::bar()" << std::endl; }
    };
    
    template<typename T_FUNC_PTR, typename T_CLASS>
    void exec(T_FUNC_PTR ptr, T_CLASS& inst)
    {
      (inst.*ptr)();
    }
    
    int main(void)
    {
      foo inst;
      exec(&foo::bar, inst);
    }
    

    【讨论】:

    • 您的第一句话暗示只有类型可以用作模板参数。这不是真的。 C++ 的模板系统也接受非类型参数。但这些必须是整数类型的编译时常量或指向具有外部链接的指针/引用。
    • @sellibitze,真的吗?我明确表示该变量是变量而不是类型...我想我会更明确一点...
    • 感谢您的回复。如上所述,我希望我的成员函数指针是非类型模板参数。当像在我的“绑定”调用中那样传递它时,没有问题 - 但我希望有一个更易于使用的不带模板参数的包装函数“示例”。示例应获取 MemberFunctionPointer 作为参数并将其作为非类型模板参数传递给“Bind”。它总是指向同一个函数,我想让编译器在编译时知道它。这可能吗?
    • @Micro,这就是我的示例正在做的事情,当exec 被调用时,我没有明确指定类型,编译器会根据参数进行计算。在你的代码中,你不需要Bind&lt;T_FUNC_PTR&gt;,编译器可以根据传入的参数(fPtr)来解决它
    • @Nim,如果我错了,请纠正我,但在您的代码执行中,我无法使用 ptr 作为非类型模板参数,这是我想要为调用 Bind 所做的。我不想传递函数指针的类型,而是函数指针本身作为参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-24
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 2013-03-30
    • 2014-06-07
    • 2015-04-24
    相关资源
    最近更新 更多