11

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.

Aamir
  • 1,974
  • 1
  • 14
  • 18
Bilal Ahmed
  • 381
  • 2
  • 13
  • 1
    Unless there's some obscure rule at play here, this looks like a GCC bug (that you should report to them). – HolyBlackCat May 24 '23 at 18:01
  • To get a pointer to a member, *any* member (function or variable, static or non-static) you should use the class, the scope operator `::`, and the pointer-to operator `&`. As in `&TestMessagePoller::poll_timer`. – Some programmer dude May 24 '23 at 18:02
  • 2
    @Someprogrammerdude Do you have a citation for this applying to static members? Every compiler I tried accepts the code if you remove the `using` and make the member `public` in the first place. – HolyBlackCat May 24 '23 at 18:05
  • @Someprogrammerdude, but how about other compilers not producing this error? – Bilal Ahmed May 24 '23 at 18:29
  • @HolyBlackCat, I've been scratching my head since the morning (8:30pm now my time) and couldn't find any reasonable cause for this issue. – Bilal Ahmed May 24 '23 at 18:30
  • A workaround: `std::cout << (void*)std::remove_reference_t::poll_timer` – Ted Lyngmo May 24 '23 at 19:15
  • @TedLyngmo, unfortunately I want my code to be backwards compatible with c++98. – Bilal Ahmed May 24 '23 at 20:17
  • @BilalAhmed How are you planning to do that with `std::shared_ptr` and `using` that didn't even exist before C++11? The code you've shown requires at least C++11. – Ted Lyngmo May 24 '23 at 20:18
  • @TedLyngmo, we're using boost in my company's source code. The code I posted is just an example to clarify the issue – Bilal Ahmed May 24 '23 at 20:22
  • If C++98 compatibility is a requirement, then post an example using C++98 (and boost). Otherwise, all workarounds you'll get will require at least C++11. – Ted Lyngmo May 24 '23 at 20:23
  • @TedLyngmo, you're correct. At first, I didn't think it will be an issue with compiler that's why I didn't care too much about c++98 – Bilal Ahmed May 24 '23 at 20:25
  • `m_sut->poll_timer` is not a static function since C++11, so gcc is actually correct. Using -> on static members equals to usage on non-static, the rest is implementation-defined. If `m_sut` is a `nullptr`, you get an UB. Also, returned value tby clang isn't equivalent of pointer to member function, it's some odd value. – Swift - Friday Pie May 25 '23 at 07:36

2 Answers2

10

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Thanks for your help, do you have a suggestion for fixing my program without downgrading the version? – Bilal Ahmed May 24 '23 at 19:45
  • 1
    @BilalAhmed You could use the workaround I presented in a comment under your question. It works in all the major compilers. – Ted Lyngmo May 24 '23 at 20:12
  • message from GCC though: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Note: member function is a different type category with different , but I yet to find a reference to this. It seems to be rather a misconception. This "bug" exist for almost 10 years and they know of it – Swift - Friday Pie May 25 '23 at 07:05
  • 1
    @Swift-FridayPie exactly, `m_sut->poll_timer` isn't a bound member function, it's a function lvalue. So the error message is misleading as well as incorrect, which points to the nature of the bug. – ecatmur May 25 '23 at 13:37
  • @ecatmur The thing is that use of -> normally means bound member and this is an edge case where it doesn't.Thing rides on a pure technicality of the case when we deal with name is introduced by `using`; And fixing it may break GCC's logic that it syntactically checks templates even if substitution doesn't happen. Well, related bug once existed in MSVC compiler (it erroneously accepted a template code which takes address of non=-static member function this way), I have no idea if clang got one. – Swift - Friday Pie May 26 '23 at 08:25
1

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.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42