Is this a bug in GCC?
Asked Answered
S

1

6

EDIT: This is not a bug, just me not knowing about dependent name lookups in templated base classes (which MSVC "helpfully" resolves without errors).


I wrote a functor implementation a while back, and a simple "Event" wrapper that uses it. It compiles fine under MSVC, but GCC gives an error about a member variable in the base class, subscribers, not being declared; changing subscribers to this->subscribers resolves the issue(!). It appears to happen only with the curiously recurring template pattern, and with partial template specialization.

Simplified source (sorry for the mind-bending template usage...):

#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;
}

Am I invoking undefined behaviour somewhere? Is my syntax somehow wrong? Is this really a bug in GCC? Is it perhaps a known bug? Any insight would be appreciated!

More details: Compiled using g++ -std=c++11 main.cpp. I'm using GCC version 4.7.2. Exact error message:

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
Sob answered 9/5, 2013 at 21:8 Comment(2)
I've ran into this too. As a rule of thumb, if it involves templates, always trust gcc over msvc (I find that gcc is right 99.9 percent of the time).Spoilt
@JesseGood There is even a duplicate SO question from yet another MSVC refugee. Apparently, gcc fixed it somewhere in the 3.4 release, breaking some old code in the process. But hey, who said vendors need bug-compatibility as a selling point?Brooklynese
B
9

This is a bug in MSVC instead. Names from dependent base classes have to be "thisambiguated".

The reason is that unqualified lookup of dependent names proceeds in two phases. During the first phase, the base class is not yet known and the compiler cannot resolve the name. MSVC does not implement two-phase name lookup and delays the lookup until the second phase.

The full specialization

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

does not suffer from this problem, because both the class and its base are regular classes, not class templates, and there is no template dependency to begin with.

When porting C++ code from MSVC to gcc/Clang, dependent name lookup disambiguation and the template keyword disambiguation (i.e. calling member function template using ::template, ->template or .template syntax) are two of the subtleties that you have to deal with (empty base optimization is another one). For all the Standards compliance rhetoric, this will probably never be fixed for reasons of backwards compatibility.

Brooklynese answered 9/5, 2013 at 21:10 Comment(5)
Could you explain further? Why isn't it an error in the last case? (Nice pun btw.)Sob
@Cameron: The last case is not a template, it's a full specialization. Everything about it is known at compile-time.Accompaniment
@Sob The C++ FAQ has an explanation of why this-> is required in that case. It works on MSVC because it doesn't implement two-phase name lookup for templates.Beaudette
Aha, thanks all. Silly me for jumping to conclusions about compiler bugs. Yet another corner of C++ that I never knew existed :-)Sob
@Sob I've been bitten by this too!Brooklynese

© 2022 - 2024 — McMap. All rights reserved.