5

I'm having a hard time understanding why the following snippet compiles. I have a template class ptr_to_member<T> which stores a pointer to a member function of T. I am then creating a new class my_class which has a ptr_to_member<my_class>. I expected the latter to cause a compilation error given that my_class is still being defined. Any help is appreciated. Thank you.

#include <iostream>

// this class simply stores a pointer to a member function of T
template <class T>
struct ptr_to_member {

    using ptr_type = void (T::*)();
    ptr_type m_ptr;

    ptr_to_member()
    : m_ptr(&T::f){}

    auto get_ptr() const {
        return m_ptr;
    }
};

// my_class has a ptr_to_member<my_class>
class my_class {

    ptr_to_member<my_class> m_ptr_to_member; // why does this compile?

public:

    void g() {
        auto ptr = m_ptr_to_member.get_ptr();
        (this->*ptr)();
    }

    void f() {
        std::cout << "f" << std::endl;
    }
};

int main() {

    my_class x;
    x.g();

}
linuxfever
  • 3,763
  • 2
  • 19
  • 43

1 Answers1

0

While defining a class, that class can be used as if it were forward declared. Since ptr_to_member only uses pointers to T until you instantiate one of it's methods, it doesn't complain. Try adding a member T rather than a pointer and you will see that it fails. The intuitive way of seeing it is that you don't need to know what T is to use a pointer of T, only that T is a type. Pointers behave the same way regardless of what they point to until you dereference them.

template <class T>
struct ptr_to_member {

    using ptr_type = void (T::*)();
    ptr_type m_ptr;

    ptr_to_member()
        : m_ptr(&T::f) {}

    auto get_ptr() const {
        return m_ptr;
    }

    T member; // Add this and see that it fails to compile
};

See this answer for more information on when you can and can't use forward declared types.

Community
  • 1
  • 1
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • Given that `my_class` is an incomplete type within its definition, I thought that I am only allowed to use pointers or references to it and not any methods. So if I had a pointer to `my_class` I would not be able to use `my_class_ptr->f()` before `my_class` is defined. I was therefore expecting that using `&T::f` in the constructor of `ptr_to_member` would create a problem... what am I missing? – linuxfever Jan 10 '17 at 23:11
  • The template in this case is a red herring. Method definitions are only looked at after the class declaration is finished. I think [this answer](http://stackoverflow.com/a/13095136/7359094) can explain it better than I can in a comment. – François Andrieux Jan 10 '17 at 23:18
  • Thank you, I think I get it now – linuxfever Jan 10 '17 at 23:25