6

Suppose I define a template T that uses a nested class of the template parameter P, as follows:

template<class P> class T
{
public:
    T(P& p) : p(p) {}
    P& p;
    typename P::Nested& get_nested()    { return p.nested; }
};

If I declare a class A that includes a nested class named Nested, I can define a variable of type T<A> with no problem:

class A
{
public:
    class Nested
    {
    public:
        int i;
    };
    Nested nested;
};

void test2a()
{
    A a;
    a.nested.i = 1;
    T<A> t_a(a);
    t_a.get_nested().i = 2;
}

Now, I want to declare a class B that, in the same way, includes a nested class named Nested and that inherits from T<B>, as follows:

class B : public T<B>
{
public:
    class Nested
    {
    public:
        int i;
    };
    Nested nested;
};

Compilation of the above code fails with error: "Nested is not a member of B"

I think I understand what's happening: at the time the template is entered, class B is incompletely defined because of inheritance.

However, I am wondering if there is any way to do such a thing...

Thanks for help.

2 Answers 2

8

You need to defer the resolution of the return type of get_nested until it is called.

One way to do this is to make the return type dependent on a template parameter:

  template<typename unused = void>
    typename std::conditional<false, unused, P>::type::Nested&
    get_nested()    { return p.nested; }

Another way (since C++14) is to use return type deduction:

  auto& get_nested()    { return p.nested; }
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the solution and it worked great! I don't really your explanation that "defer the resolution of the return type of get_nested until it is called". Do you mean it's resolved at running time? But I thought all template including get_nested is expanded at running time?
No, this all happens at compile time - but compile time is split into "phases". What's going on here is that template instantiations occur later in compilation, after the whole file is read.
looks like that the linked "Phases of translation" didn't length about template/ dependent name lookup. But I'm guessing that in order to make the example work, nested template are looked up after the whole file is read (and therefore the class definitions are completed)? Was this due to sfinae (Because you put into std::conditional trait)
Yes, the return type is being looked up after the whole file is read and so the class definitions are complete. No, it's not dependent on SFINAE - although because of SFINAE, if something goes wrong the error will be that get_nested cannot be resolved, rather than that P::Nested cannot be resolved.
4

I was able to have your example compile with simply

template<class P> class T
{
public:
    T(P& p) : p(p) {}
    P& p;
    auto& get_nested()    { return p.nested; }
};

Another approach, exploiting the same trick as @ecatmur, but a bit simpler:

template<class R = P>
typename R::Nested& get_nested()    { return p.nested; }

Similarly, here the compiler has to postpone evaluation of P::Nested until you call get_nested().

7 Comments

Thank you very much for these tricks, both Petr and ecatmur. However, I am using MSVC 2010 compiler which wont compile any of them. I am seriously thinking about upgrading to C++14 compliant compiler... now. Anyway, thanks a lot for your answers.
@SharpDressedMan, what's the exact problem and exact error message with my second solution? It seems to be even C++03 for me...
Error is <b>C4519: default template arguments are only allowed on a class template</b>.
I can change this error to a warning using "#pragma warning (1 : 4519)" but compilation fails a bit later on line "t_a.get_nested().i = 2;", with error C2783: "could not deduce template argument for 'R'". Error fixed using "t_a.get_nested<A>().i", but not that great. This works, anyway, thanks.
@SharpDressedMan, try to make get_nested a functor then. I will probably add such a solution when I get to a computer (I'm on tablet now)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.