GCC编译器产生错误:在CLang编译器中,成员函数的使用无效。

huangapple go评论68阅读模式
英文:

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

问题

在主函数中,我想要打印出poll_timer函数的地址。这个程序在使用Clang编译时可以成功运行,但在使用GCC编译时则出现以下错误:

"709568706/source.cpp: 在函数‘int main()’中:
709568706/source.cpp:28:32: 错误:无效使用成员函数‘static void MessagePoller::poll_timer()’(您是否忘记了‘()’?)
     std::cout << (void*)m_sut->poll_timer << std::endl;
                         ~~~~~~~^~~~~~~~~~"

我尝试了一种方法,即删除"using"语句并将poll_timer的访问权限更改为public,这样可以正常工作。但我想知道原程序的问题出在哪里。

以下是原程序的中文翻译部分:

#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'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

&quot;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 &lt;&lt; (void*)m_sut-&gt;poll_timer &lt;&lt; std::endl;
                         ~~~~~~~^~~~~~~~~~&quot;
#include &lt;iostream&gt;
#include &lt;memory&gt;

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

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

};
typedef std::shared_ptr&lt;TestMessagePoller&gt; TestMessagePollerPtr;

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

    std::cout &lt;&lt; &quot;HERE1\n&quot;;
    m_sut-&gt;poll_timer();
    std::cout &lt;&lt; (void*)m_sut-&gt;poll_timer &lt;&lt; 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.

答案1

得分: 10

这是gcc中的一个错误;我已经提交了https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109958。

这个错误似乎首次出现在4.8分支中,所以如果可能的话,你可以考虑降级到4.7.4版本。

解决方法包括:
(正如你所观察到的)使用公共继承,这样你就不必使用using-declaration*;

  • 使用nested-name-specifier,明确命名类:&TestMessagePoller::poll_timer
  • 同样,但使用decltype,即:&std::remove_cvref_t<decltype(*m_sut)>::poll_timer
  • 编写一个包装器:static void poll_timer() { MessagePoller::poll_timer(); }

这段代码是有效的;&amp; 可以在 类成员访问操作(.->)上使用,其中操作数指定了一个静态成员函数(但不可以 用于显式对象成员函数)。非静态成员函数在这里明确被排除

英文:

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: &amp;TestMessagePoller::poll_timer
  • same, but with decltype, i.e.: &amp;std::remove_cvref_t&lt;decltype(*m_sut)&gt;::poll_timer
  • write a wrapper: static void poll_timer() { MessagePoller::poll_timer(); }

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

答案2

得分: 1

If you change line to

  std::cout &lt;&lt; (void*)(&amp;m_sut-&gt;poll_timer) &lt;&lt; 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 &#39;&amp;TestMessagePoller::poll_timer&#39; [-fpermissive]

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-&gt;poll_timer is? poll_timer is a member function, operator-&gt; in this case be an equivalent of

m_sut-&gt;*std::remove_reference_t&lt;decltype(*m_sut)&gt;::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 -&gt; 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.

英文:

If you change line to

  std::cout &lt;&lt; (void*)(&amp;m_sut-&gt;poll_timer) &lt;&lt; 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 &#39;&amp;TestMessagePoller::poll_timer&#39; [-fpermissive]
     std::cout &lt;&lt; (void*)(&amp;m_sut-&gt;poll_timer) &lt;&lt; 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-&gt;poll_timer is? poll_timer is a member function, operator-&gt; in this case be an equivalent of

m_sut-&gt;*std::remove_reference_t&lt;decltype(*m_sut)&gt;::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 -&gt; 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.

huangapple
  • 本文由 发表于 2023年5月25日 01:54:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76326242.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定