1

Here's a minimal example I could come up with:

template <typename T>
struct Util
{
    using Type = int;
};

struct A
{
    struct B : public Util<B> {
        void fun(Type) {}
    };
};

template <typename T>
struct C
{
    struct D : public Util<D> {
        void fun(Type) {}
    };
};

int main()
{
    A::B{}.fun(0);
    C<int>::D{}.fun(0);
}

The only difference between A::B and C::D is that C is a template.

Struct C::D fails to compile with the following error:

test_cpp.cpp:18:18: error: ‘Type’ has not been declared
         void fun(Type) {}
                  ^~~~

Why does this fails to compile? How do I make this compile?

Assume that Util is from external library and I cannot change it (it's boost::iterator_facade if you're courious).

Adri C.S.
  • 2,909
  • 5
  • 36
  • 63
etam1024
  • 843
  • 1
  • 7
  • 17

1 Answers1

3

At this point:

template <typename T>
struct C
{
    struct D : public Util<D> {
        void fun(Type) {} // <-- here
    };
};

The compiler has no way of knowing * that Type needs to come from Util<D>'s scope. It's the same issue with calling base class member functions, which need to be qualified explicitly with this-> or they won't be found.

You should be able to fix this by explicitly qualifying the type:

void fun(typename Util<D>::Type) {}

See complete snippet on Coliru.

The reason behind the difference is, as @etam1024 correctly points out in a comment, that in A::B, Type is a non-dependent name, and in C::D it is a dependent name. See for an explanation of a similar situation regarding member function qualification of base class member functions this answer.

* In my opinion, the compiler can know where to look for Type, but the language rules say it doesn't look that far. Note I might be overlooking special cases where it is impossible for the compiler to deduce this, hence leading to the general case requiring full koalafication.

Community
  • 1
  • 1
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • Thanks! I'd add that in `A::B` the `Type` is a "nondependent name" and in `C::D` it is a "dependent name" (it depends on template argument). https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-types – etam1024 Aug 03 '16 at 08:11