【问题标题】:How to instantiate a member function pointer?如何实例化成员函数指针?
【发布时间】:2016-11-20 14:02:00
【问题描述】:

假设我有以下代码

template<class MemberFunc>
class Foo {
  MyClass object_;
  void call() {
    auto ptr = MemberFunc{};
    (object_.*ptr)();
  }
};


int main() {
  Foo<decltype(&MyClass::doThings)> foo;
  foo.call();
}

这段代码对我来说确实崩溃了,因为 ptr 为 0。为什么成员函数构造函数返回 0?

我的解决方法如下,但它涉及代码重复。没有其他方法可以从类型构造/实例化成员函数吗?欢迎使用 C++14。

template<class MemberFunc, MemberFunc f> 
class Foo {
  MyClass object_;
  void call() {
    (object_.*f)();
  }
};


int main() {
  Foo<decltype(&MyClass::doThings), &MyClass::doThings> foo;
  foo.call();
}

【问题讨论】:

  • 我什至不明白为什么第一个 sn-p 编译。指向成员的函数是非类型模板参数,因此它不应该与模板声明中的class 一起使用。
  • @vsoftco decltype 产生一个类型
  • @krzaq 我错过了decltype,很好,谢谢!现在有趣的是:gcc 6 不编译代码,抱怨ptr 的“不完整类型”,直播here。另一方面,Clang 3.7 编译它,请参阅here(当然正如它崩溃的答案中提到的那样)。
  • #define make_foo(func) Foo&lt;decltype(func), func&gt;() 然后auto foo = make_foo(&amp;MyClass::doThings)

标签: c++ c++14


【解决方案1】:

为什么成员函数构造函数返回0?

因为它是一个指针 (-to-member-function),并且 所有 标量类型的值初始化为 0 (C++14 [dcl.init]/8.4)。

没有其他方法可以从类型构造/实例化成员函数吗?

您可以拥有多个具有相同签名的成员函数;它怎么知道你要引用哪个成员函数?

您拥有的代码适用于 C++14。在 C++17 中,可以简写为:

template<auto f> 
class Foo {
  MyClass object_;
  void call() {
    (object_.*f)();
  }
};

int main() {
  Foo<&MyClass::doThings> foo;
  foo.call();
}

【讨论】:

  • 据我所知,通过 decltype(&MyClass::doThings) 我不会删除成员函数的实际类型。这就是 std::function 所做的,但这在这里没有发生。所以编译器确实知道它是哪个成员函数。
  • @AlexanderTheißen:decltype(&amp;MyClass::doThings) 的类型是 pointer-to-member-function。它将保留 signaturedoThings 的返回类型、参数类型和 const-ness),但不会保留 value(具有正在引用签名)。如果你展示 MyClass::doThings 的样子,我会给你具体的例子来说明我的意思。
【解决方案2】:

您不能从其类型实例化成员函数。例如,考虑以下类:

struct foo
{
    void bar(int){}
    void baz(int){}
};

假设你有一个类型void (foo::*)(int)。您想从中获得哪个功能?

从 C++1z 开始,您将能够使用 auto 推导出非类型、非模板模板参数:

template<auto f> 
class Foo {
  MyClass object_;
  void call() {
    (object_.*f)();
  }
};


int main() {
  Foo<&MyClass::doThings> foo;
  foo.call();
}

demo


我能想到的 C++11/14 的唯一解决方法是使用宏:

#define type_value_pair(x) decltype(x), x
template<class MemberFunc, MemberFunc f> 
class Foo {
  MyClass object_;
  void call() {
    (object_.*f)();
  }
};


int main() {
  Foo<type_value_pair(&MyClass::doThings)> foo;
  foo.call();
}

demo

但出于可读性原因,我建议不要使用它。

【讨论】:

    【解决方案3】:

    decltype 返回其参数的类型。使用以下示例:

    class MyClass {
    
    public:
        int foo();
        int bar();
    };
    

    两者

    decltype(&MyClass::foo);
    

    decltype(&MyClass::bar);
    

    是同一类型:int (MyClass::*)()

    当您默认初始化此类型时,默认初始化结果为nullptr。因此崩溃了。

    【讨论】:

      【解决方案4】:

      当你对一个指向成员函数的指针进行值初始化时,你当然会收到一个 nullptr。

      顺便说一句,您可以拥有多个具有相同类型的成员函数,就像您在 Sam 的回答中看到的那样。

      不知道为什么要把MemberFunc放到Foo的模板参数列表中。

      但是,如果你想创建一个函数来调用它,这可能是 C++1z 之前的 C++ 中更好的方法(在 C++1z 中使用 auto 更适合这个问题):

      class Foo {
        MyClass object_;
      public:
        template<class MemberFunc>
        void call(MemberFunc ptr) {
          (object_.*ptr)();
        }
      };
      int main() {
        Foo foo;
        foo.call(&MyClass::doThings);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-05-20
        • 1970-01-01
        • 2015-06-28
        • 1970-01-01
        • 1970-01-01
        • 2010-12-26
        • 1970-01-01
        相关资源
        最近更新 更多