GCC compiler produces error: invalid use of member function while CLang compiler does NOT

444 views Asked by At

I'm using the following program:

In the main function, I want to print the address of the poll_timer function.

The program compiles and runs successfully with clang but not with GCC.

I get the following error with GCC

"709568706/source.cpp: In function ‘int main()’:
709568706/source.cpp:28:32: error: invalid use of member function ‘static void MessagePoller::poll_timer()’ (did you forget the ‘()’ ?)
     std::cout << (void*)m_sut->poll_timer << std::endl;
                         ~~~~~~~^~~~~~~~~~"
#include <iostream>
#include <memory>

class MessagePoller
{
  protected:
    static void poll_timer()
    {
        std::cout << "Poll timer Base called\n";
    }
};

class TestMessagePoller : public MessagePoller
{
public:
    using MessagePoller::poll_timer;

};
typedef std::shared_ptr<TestMessagePoller> TestMessagePollerPtr;

int main()
{   
    TestMessagePollerPtr m_sut;
    m_sut = TestMessagePollerPtr(new TestMessagePoller());

    std::cout << "HERE1\n";
    m_sut->poll_timer();
    std::cout << (void*)m_sut->poll_timer << std::endl;

    return 0;
    
}

I have tried one thing, removing the "using" statement and changing the access of the poll_timer to public and that worked. But I would like to know what is going on with the program as is.

2

There are 2 answers

5
ecatmur On BEST ANSWER

Yes, this is a bug in gcc; I've filed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109958.

The bug appears to have first appeared in the 4.8 branch, so you might consider downgrading to 4.7.4 if that's an option.

Workarounds are:

  • (as you've observed) use public inheritance so that you don't have to use a using-declaration;
  • use a nested-name-specifier instead, explicitly naming the class: &TestMessagePoller::poll_timer
  • same, but with decltype, i.e.: &std::remove_cvref_t<decltype(*m_sut)>::poll_timer
  • write a wrapper: static void poll_timer() { MessagePoller::poll_timer(); }

The code is valid; & may be used on a class member access operation (. or ->) where the operand designates a static member function (but not an explicit object member function). Non-static member functions are explicitly excluded here.

0
Swift - Friday Pie On

If you change line to

  std::cout << (void*)(&m_sut->poll_timer) << std::endl;

gcc would respond with

prog.cc:29:34: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function.  Say '&TestMessagePoller::poll_timer' [-fpermissive]
     std::cout << (void*)(&m_sut->poll_timer) << std::endl;

Ths happens even with gcc v.4.9. And I remember that it wouldn't accept original code either, as early as 4.5. Clang would compile though.

Compiler bugs aside, there is a formal problem here.. what type expression m_sut->poll_timer is? poll_timer is a member function, operator-> in this case be an equivalent of

m_sut->*std::remove_reference_t<decltype(*m_sut)>::poll_timer

Resulting type is a callable. You can only use operator() on it meaningfully and that's it. type::function_name is a pointer to member function, a separate type from a pointer to member.

Since C++11 using -> on static members equals to usage on a non-static member and requires class pointer to be correct. The rest is implementation-defined,so gcc is actually correct in not allowing to obtain address. It wouldn't be wrong to allow it either. If m_sut is a nullptr, formally you get an UB. That's where gcc's devs logic comes from, which results in this bug.

Afaik what happens here is a gap in standard's wording itself. It doesn't forbid or allow this particular expression explicitly, but such use doesn't make sense. The main problem is that GCC is inconsistent with this. If poll_timer is public and no using declaration appears in derived class, code works. With appearance of using declaration formally memory model of derived class deviates from standard model and something breaks in compiler. Such inconsistency IS a bug.