这是GCC中的一个bug吗?

Is this a bug in GCC?

本文关键字:一个 bug GCC 这是      更新时间:2023-10-16

编辑:这不是一个bug,只是我不知道模板基类中的依赖名称查找(MSVC"有用"地解决了而没有错误)。


不久前,我写了一个函函数实现,以及一个使用它的简单"事件"包装器。它在MSVC下编译得很好,但是GCC给出了一个关于基类subscribers中成员变量未被声明的错误;将subscribers更改为this->subscribers解决了这个问题(!)。它似乎只发生在奇怪地重复出现的模板模式和部分模板专门化中。

简化的源代码(很抱歉使用令人费解的模板…):

#include <vector>
template<typename TEvent>
struct EventBase
{
protected:
        std::vector<int> subscribers;
};
template<typename TArg1 = void, typename TArg2 = void>
struct Event : public EventBase<Event<TArg1, TArg2> >
{
        void trigger(TArg1 arg1, TArg2 arg2) const
        {
                // Error on next line
                auto it = subscribers.cbegin();
        }
};
template<typename TArg1>
struct Event<TArg1, void> : public EventBase<Event<TArg1> >
{
        void trigger(TArg1 arg1) const
        {
                // Using `this` fixes error(?!)
                auto it = this->subscribers.cbegin();
        }
};
template<>
struct Event<void, void> : public EventBase<Event<> >
{
        void trigger() const
        {
                // No error here even without `this`, for some reason!
                auto it = subscribers.cbegin();
        }
};
int main()
{
        return 0;
}

我在某处调用未定义行为吗?我的语法有问题吗?这真的是GCC中的一个bug吗?也许这是一个已知的bug?任何见解将不胜感激!

更多细节:使用g++ -std=c++11 main.cpp编译。我使用的是GCC 4.7.2版本。确切的错误信息:

main.cpp: In member function ‘void Event<TArg1, TArg2>::trigger(TArg1, TArg2) const’:
main.cpp:17:15: error: ‘subscribers’ was not declared in this scope

这是MSVC而不是中的一个错误。来自依赖基类的名称必须是"有歧义的"。

原因是依赖名称的非限定查找分两个阶段进行。在第一阶段,基类还不知道,编译器无法解析其名称。MSVC不实现两阶段名称查找,并将查找延迟到第二阶段。

完全专门化

template<>
struct Event<void, void> : public EventBase<Event<> >
{
        void trigger() const
        {
                // No error here even without `this`, for some reason!
                auto it = subscribers.cbegin();
        }
};

没有这个问题,因为类和它的基类都是普通的类,而不是类模板,而且一开始就没有模板依赖关系。

当将c++代码从MSVC移植到gcc/Clang时,依赖名称查找消歧和template关键字消歧(即使用::template, ->template.template语法调用成员函数模板)是您必须处理的两个微妙之处(空基优化是另一个)。对于所有符合标准的修辞,由于向后兼容性的原因,这可能永远不会被修复。