GCC编译器产生错误:成员函数使用无效,而CLang编译器不

093gszye  于 2023-05-29  发布在  其他
关注(0)|答案(2)|浏览(185)

我正在使用以下程序:
在main函数中,我想打印poll_timer函数的地址。
该程序使用clang成功编译和运行,但使用GCC则不行。
我在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;
    
}

我已经尝试了一件事,删除***“using”***语句,并将poll_timer的访问权限更改为public,并且成功了。但我想知道这个项目现在是怎么回事。

hjqgdpho

hjqgdpho1#

是的,这是GCC中的一个bug;我已经提交了https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109958
这个bug似乎是在4.8分支中首次出现的,所以如果可以的话,你可以考虑降级到4.7.4。
解决方法包括:

  • (正如您所观察到的)使用公共继承,这样就不必使用 using-declaration;
  • 使用 nested-name-specifier,显式命名类:&TestMessagePoller::poll_timer
  • 相同,但使用decltype,即:&std::remove_cvref_t<decltype(*m_sut)>::poll_timer
  • 写一个wrapper:static void poll_timer() { MessagePoller::poll_timer(); }

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

polhcujo

polhcujo2#

如果将行改为

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

gcc将以

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;

即使在gcc v.4.9中也会发生这种情况。我记得它也不接受原始代码,早在4.5版本。Clang会编译。
除了编译器错误之外,这里还有一个形式上的问题。m_sut->poll_timer是什么类型的表达式?poll_timer是一个成员函数,在这种情况下,operator->是一个等价的
m_sut->*std::remove_reference_t<decltype(*m_sut)>::poll_timer
结果类型是可调用的。你只能在上面有意义地使用operator(),仅此而已。type::function_name是一个指向成员函数的指针,它是一个独立于指向成员的指针的类型。
因为C++11在静态成员上使用->等同于在非静态成员上使用,并且要求类指针正确。其余的是实现定义的,所以gcc不允许获取地址实际上是正确的。允许也不会错。如果m_sut是一个nullptr,形式上你得到一个UB。这就是gcc的开发者逻辑的来源,这导致了这个bug。
这里所发生的是标准措辞本身的差距。它没有明确禁止或允许这个特定的表达式,但是这样的使用没有意义。主要问题是GCC与此不一致。如果poll_timer是public的,并且派生类中没有出现using声明,则代码可以工作。随着形式化声明的出现,派生类的内存模型偏离了标准模型,编译器出现了一些问题。这种不一致性是一个bug。

相关问题