16

I have the following code:

template <class T>
class Outer
{
public:
    Outer();

    template <class U>
    void templateFunc()
    {
    }

private:
    class Inner
    {
    public:
        Inner(Outer& outer)
        {
            outer.templateFunc<int>();
            Outer* outer_ptr = &outer;
            [outer_ptr]() 
            {
                outer_ptr->templateFunc<int>();
            }();
        }
    };

    Inner m_inner;
};

template <class T>
Outer<T>::Outer()
    : m_inner(*this)
{
}

int main()
{
    Outer<double> outer;
}

As you can see, there is a template class that contains a nested class, which in constructor calls some template method of its enclosing class. AFAIK, even though enclosing class is a template class - for the nested class it is a non-dependent name, so calling its template method without template should not be a problem. The problem happens when I define a lambda inside nested class' constructor, capture pointer to outer class, and try to call the same template method - g++7.2 gives the following compilation error:

prog.cc: In lambda function:

prog.cc:22:41: error: expected primary-expression before 'int'
                outer_ptr->templateFunc<int>();
                                        ^~~
prog.cc:22:41: error: expected ';' before 'int'

However, g++-5.4 and g++-6.3 compile this code just fine. So it seems that g++-7.2 treats the outer_ptr's type inside the lambda as a dependent name - and I cannot understand why. Can someone explain this to me?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    [Clang and MSVC both accepts this](https://godbolt.org/g/5ejzmG) – Passer By Nov 13 '17 at 10:09
  • Considering previous gcc accepts it, I'm guessing it really is a bug. – Passer By Nov 13 '17 at 10:11
  • So it is probably a bug in g++-7.2? – IvanIvanovich Nov 13 '17 at 10:11
  • 2
    [Further evidence this is a bug](https://godbolt.org/g/YYg4Dg). Captured variables won't compile while using the injected class name directly does – Passer By Nov 13 '17 at 10:17
  • Just theorizing a little: looks like g++-7.2 transforms lambda into a template class, where template arguments are types of captured variables, therefore captured variables are considered dependent names. I am not sure, but maybe this behavior does not violate the standard, and its just that Clang, MSVC and earlier versions do this differently - but this does not mean that g++-7.2 is wrong? – IvanIvanovich Nov 13 '17 at 10:26
  • 2
    I wouldn't be sure if this is a bug... [lambda expression need to be free of context of its declaration](https://stackoverflow.com/a/40243927/4324224)... – W.F. Nov 13 '17 at 10:28
  • @W.F. - do I understand you correctly, that the lambda may be partially instantiated before the instantiation of `Outer` happens, and in order to be able to perform this instantiation - compiler has to treat `outer_ptr` as a template parameter? – IvanIvanovich Nov 13 '17 at 11:14
  • Not sure about template parameter but the instantiation definitely has to/can occur... The question is what are the compiler options to deal with the problem and if the additional template parameter to keep the type information about the capture (stored in its field) isn't its only choice... – W.F. Nov 13 '17 at 11:28

1 Answers1

2

Yes, this is a gcc regression. Filed as 82980.

Here's a reduced example:

template <class T>
struct Outer
{
    template <class U>
    void f();

    void bar(Outer outer) {
        [outer](){ outer.f<int>(); };
    }
};

int main() { }

outer.f is member access for the current instantiation, so that expression shouldn't count as type dependent, so you shouldn't need to provide the template keyword.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thank you for the answer! You seem like a guy who is familiar with a standard, so maybe it won't be hard for you to clarify related question: in the [comment](https://stackoverflow.com/questions/47261553/non-dependent-name-lookup-and-lambda/47277062#comment81475104_47261553) there was a link to another SO [question](https://stackoverflow.com/a/40243927/4324224), where it is stated that lambda may be partially instantiated before the instantiation of its enclosing template class/function. May this somehow relax the rules of what counts as type dependent for lambdas? – IvanIvanovich Nov 14 '17 at 06:38
  • Actually, I believe I misunderstood what was written in the answer to the mentioned question - it was about generic lambdas being partially instantiated **with** the instantiation of enclosing class/function, not **before**. – IvanIvanovich Nov 14 '17 at 07:20