Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a bug in GCC?

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
like image 599
Cameron Avatar asked May 09 '13 21:05

Cameron


1 Answers

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.

like image 88
TemplateRex Avatar answered Oct 16 '22 14:10

TemplateRex