0

Can someone explain to me why gcc outputs this error?

 /media/projects/src/libs/GUIElements/include/GUIElements/TemplatedFlagManager.h:195:
 error: ‘ClearQueue’ was not declared in this scope
             ClearQueue<QueueType>();
               ^     
/media/projects/src/libs/GUIElements/include/GUIElements/TemplatedFlagManager.h:35: error: 
    ‘typename std::enable_if<(I < sizeof... (Tp)), void>::type for_each(std::tuple<_Args2 ...>&, FuncT) 
    [with long unsigned int I = 1ul; FuncT = FlagNotifier<Args>::AddSubscriber(QSharedPointer<FlagSubscriber>)
    [with Args = {SystemFlags::EAppFlags, SystemFlags::EQueueFlags, SystemFlags::EMessageFlags}]::<lambda(auto:2)>;
    Tp = {QList<SystemFlags::EAppFlags>, QList<SystemFlags::EQueueFlags>, QList<SystemFlags::EMessageFlags>}; 
    typename std::enable_if<(I < sizeof... (Tp)), void>::type = void]’,
    declared using local type ‘FlagNotifier<Args>::AddSubscriber(QSharedPointer<FlagSubscriber>)
    [with Args = {SystemFlags::EAppFlags, SystemFlags::EQueueFlags, SystemFlags::EMessageFlags}]::<lambda(auto:2)>’, 
    is used but never defined [-fpermissive]
    for_each(std::tuple<Tp...>& t, FuncT f)
    ^

in this code:

template <typename... Args>
class FlagNotifier
{
....

    void AddSubscriber(QSharedPointer<FlagSubscriber> sub)
    {
        for_each(flagQueues, [&](auto queue){
        using QueueType = typename decltype(queue)::value_type;
        for(auto flag : queue)
        {
            auto& group = std::get<get_index<QueueType, Args...>::value>(flagGroups);
            group[flag].push_back(sub);
        }

        ClearQueue<QueueType>();
        sub->ApplyState();
    });
    }   

    template <typename T>
    void ClearQueue(){
        auto& flagQueue = std::get<get_index<T, Args...>::value>(this->flagQueues);
        flagQueue.clear();
    }
    std::tuple<QList<Args>...> flagQueues;
    std::tuple<QHash<Args, QList<QSharedPointer<FlagSubscriber>>>...> flagGroups;
}

msvc is perfectly fine with it. The "auto queue" is actually a member of tuple

std::tuple<QList<Args>...> flagQueues;

so the type should be easily deducible and yet this obscure warning...

P.S.

for_each is taken from this question: stackoverflow.com/a/6894436/1143162

get_index from this: stackoverflow.com/a/18063608/1143162

Zeks
  • 2,071
  • 14
  • 30
  • What is `for_each` ? The problem with `ClearQueue` seems to be that visual check content only at instantiation (1 pass vs required 2 passes). – Jarod42 Jun 27 '17 at 10:03
  • for_each is tuple iteration mechanism from here : https://stackoverflow.com/a/6894436/1143162 – Zeks Jun 27 '17 at 10:04
  • and get_index is from here: https://stackoverflow.com/a/18063608/1143162 – Zeks Jun 27 '17 at 10:06
  • 1
    Does `this->template ClearQueue();` fix the issue ? – Jarod42 Jun 27 '17 at 10:11
  • yes, it helped. thank you. Is there any link where I can read about this->template usage? – Zeks Jun 27 '17 at 12:02
  • Look at [where-and-why-do-i-have-to-put-the-template-and-typename-keywords](https://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) – Jarod42 Jun 27 '17 at 12:04

2 Answers2

3

ClearQueue<QueueType>(); is treated as non dependent name whereas you want it to treat it as dependent.

changing to

this->template ClearQueue<QueueType>();

should solve your issue.

Extra template is needed to parse correctly the expression.

Jarod42
  • 173,454
  • 13
  • 146
  • 250
2

Now, to answer the question. Jarod42 has already mentioned what is going wrong, and I'll try to explain why.

FlagNotifier is a template class. When you're invoking its member function ClearQueue, in certain contexts it may be unclear for the compiler if it should be a call to a member function or to a free function ClearQueue (let's suppose you have one).

Let's look at this simpler toy example:

#include <iostream>

template<typename T>
struct B {
    int f();
};

template<typename T>
struct D : B<T> {
    int g();
};

template<typename T>
int D<T>::g() {
    return f();  // error: should be ‘this->f()’
}

int main()
{
  return 0;
}

The reason why this should not compile is that I might have defined the specialization of class B, which has no "foo" function, for instance

template<>
struct B<int> {
};

Then the following code would get broken:

D<int> d;
d.g();

Because of that, C++ standard requires using this-> explicitly in such situations.

Now, why didn't Visual Studio complain? Because it is not compliant to the Standard regarding this rule. The reason is that this compiler was introduced long before the template specialization rules in C++ standard were stabilized, and later on Microsoft were reluctant to breaking the backwards compatibility. In Visual C++ 2017 they have introduced a special flag /permissive which enforces this rule, as well a number of other rules, you can see the details here.

Also, you can find this toy example down the links:

Vasiliy Galkin
  • 1,604
  • 11
  • 19