1

I have a class like this:

#include <iostream>

template <class T>
class LL
{
    using size_t = unsigned int;

    class Node
    {
        T m_data;
        Node* m_next;

        Node(const T& data) :m_data{ data }, m_next{ nullptr }{}

        friend std::ostream& operator<<(std::ostream& out, const Node& node)
        {
            out << node.m_data;
            return out;
        }

        friend std::ostream& operator<<(std::ostream& out, const LL& ll);

        friend class LL;
    };

    Node* m_first{ nullptr };
    size_t m_size{ 0 };

    Node* newNode(const T& data)
    {
        return new Node{ data };
    }

public:
    void push(const T& data)
    {
        Node* temp = newNode(data);
        temp->m_next = m_first;
        m_first = temp;
        ++m_size;
    }

    Node* head()
    {
        return m_first;
    }

    size_t size() const
    {
        return m_size;
    }

    ~LL()
    {
        if (m_first)
        {
            Node* trav = m_first->m_next;
            Node* foll = m_first;
            while (trav)
            {
                delete foll;
                foll = trav;
                trav = trav->m_next;
            }
            delete foll;
        }
    }

    friend std::ostream& operator<<(std::ostream& out, const LL& ll)
    {
        Node* trav = ll.m_first;
        while (trav)
        {
            out << *trav << ' ';
            trav = trav->m_next;
        }
        return out;
    }
};

I also have a function template somewhere else below this class in the same file that tries to access Node and looks like this with two compiler errors:

template <typename T>
int getSize(LL<T>::Node* node) //C2065: node is undeclared, C3861: node is not found
{
    if (node)
    {
        return 1 + getSize(node->m_next);
    }
    return 0;
} //does not compile

After sometime I tried this, again with two compiler:

template <typename T>
int getSize(LL<T>::Node<T>* node) //C2065 like before, C7510: use of dependent template name must be prefixed with 'template'
{
    if (node)
    {
        return 1 + getSize(node->m_next);
    }
    return 0;
} //does not compile

After sometime again, I tried the below which compiled fine.

template <typename T>
int getSize(typename LL<T>::template Node<T>* node)
{
    if (node)
    {
        return 1 + getSize(node->m_next);
    }
    return 0;
}

Now, when I tried to call this function from my driver function, I got compiler errors again:

int main()
{
    LL<int> ll;
    std::cout << getSize(ll.head()); //E0304, C2672 and C2783

    //E0304: no instance of the function template "getSize" matches the argument list
    //C2672: no matching overload function found
    //C2783: could not deduce template argument for 'T'
}

I tried everything that I possible could and couldn't sort this problem out. Could someone please explain me what is going on? Note: All codes that I've mentioned here are in the same file.

Abhishek A Udupa
  • 401
  • 6
  • 13

1 Answers1

4

getSize(ll.head()) fails because of non-deduced context; template parameter T can't be deduced automatically.

If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

  • 1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

The declaration should be

template <typename T>
int getSize(typename LL<T>::Node* node) // using class instead of typename works for OP (MSVC)
{
    //some code
}

And since Node is not a template you don't need to use template keyword.

LIVE


See Where and why do I have to put the “template” and “typename” keywords? about why use the keyword typename and template.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • But that still shouldn't work because `LL::Node` is `private`? – t.niese May 18 '20 at 08:13
  • @t.niese - It can work just fine when `Node` is private, with a bit of tweaking. – StoryTeller - Unslander Monica May 18 '20 at 08:14
  • @StoryTeller-UnslanderMonica Why? Because it depends on template parameter `T`? – songyuanyao May 18 '20 at 08:16
  • 1
    Because `typename LL::Node` would not work due to `private` , but something like `template int getSize(T* node)` and `getSize(ll.head())` to would work. – t.niese May 18 '20 at 08:20
  • 1
    @songyuanyao - No, directly accessing the name should not be possible. But the function can still be defined to take a node pointer, for instance `template int getSize(decltype(std::declval>().head()) node)` – StoryTeller - Unslander Monica May 18 '20 at 08:21
  • @StoryTeller-UnslanderMonica I got it. – songyuanyao May 18 '20 at 08:22
  • @StoryTeller-UnslanderMonica it's interesting that something like this works. I mean sure, the class made `Node` available by returning it, but why is it allowed to refer to the type that way, but not directly in that case. Is it just to prevent developers to do dump things and think about it, or is it a hack introduced to get around a flaw in the specs? – t.niese May 18 '20 at 08:25
  • Still it doesn't work. I've got a C2672: no matching overload function found and a C2783: could not deduce template argument for 'T' – Abhishek A Udupa May 18 '20 at 08:30
  • @t.niese - Access checks always applied to names only. For instance, in C++03 a member function could return a nested class whose name is declared private to the enclosing class. This forbade outside code from creating variables of that class, and so the proxy had to be used immediately. Then C++11 came and variable type deduction was introduced... I don't think the interaction between these features was analyzed too deeply. – StoryTeller - Unslander Monica May 18 '20 at 08:30
  • @AbhishekAUdupa I tried it [here](https://gcc.godbolt.org/z/yVokK_) and compiles fine. What's your compiler and version? – songyuanyao May 18 '20 at 08:35
  • @StoryTeller-UnslanderMonica sure, but it is still kinda strange that this allows constructing a `Node` outside of `LL`: [wandbox.org/permlink/dMOisJtCc4t3YGrA](https://wandbox.org/permlink/dMOisJtCc4t3YGrA). – t.niese May 18 '20 at 08:36
  • @songyuanyao I'm using MSVS and I tried compiling this with the C++14 and C++17 compilers. None worked – Abhishek A Udupa May 18 '20 at 08:53
  • @AbhishekAUdupa The code is valid I have no idea why your compiler doesn't compile it. (And it doesn't use any C++14 or C++17 feature.) You might upgrade the compiler. – songyuanyao May 18 '20 at 09:04
  • Finally, I got it to work. I changed `int getSize(typename LL::Node* node)` to `int getSize(class LL::Node* node)`. I don't understand why though. – Abhishek A Udupa May 18 '20 at 09:17
  • 1
    @AbhishekAUdupa I might be just a MSVC's issue. – songyuanyao May 18 '20 at 09:25