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

2

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.

Hypertonic answered 24/5, 2023 at 17:54 Comment(12)
Unless there's some obscure rule at play here, this looks like a GCC bug (that you should report to them).Minier
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.Gillis
@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.Minier
@Someprogrammerdude, but how about other compilers not producing this error?Hypertonic
@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.Hypertonic
A workaround: std::cout << (void*)std::remove_reference_t<decltype(*m_sut)>::poll_timerWolfson
@TedLyngmo, unfortunately I want my code to be backwards compatible with c++98.Hypertonic
@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.Wolfson
@TedLyngmo, we're using boost in my company's source code. The code I posted is just an example to clarify the issueHypertonic
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.Wolfson
@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++98Hypertonic
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.Pardue
E
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.

Evaluate answered 24/5, 2023 at 18:48 Comment(5)
Thanks for your help, do you have a suggestion for fixing my program without downgrading the version?Hypertonic
@BilalAhmed You could use the workaround I presented in a comment under your question. It works in all the major compilers.Wolfson
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 itPardue
@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.Evaluate
@Evaluate 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.Pardue
P
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.

Pardue answered 25/5, 2023 at 7:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.