Undefined reference to friend function template defined inside class in a namespace
Asked Answered
F

0

1

This is a follow-up on my answer to this question.

The original answer I posted does not have any namespaces, and it solves the problem. However, the OP subsequently made an edit claiming that the solution does not work if the class is inside a namespace.

My initial reaction was that you can make it work by simply having using N::f; either at global scope or inside main() (or any other function). While that is certainly the case, the OP (justifiably) commented that this is not ideal, and I agree.

Nevertheless, I still thought that calling N::f without having using N::f; should work just fine, but to my surprise I got an undefined reference error when I tried the following:

#include<iostream>

namespace N
{
    template<class T>
    class Class;

    template<typename U, typename W>
    Class<W> f (Class<U>& C, const Class<U>& D);

    template<class T>
    class Class
    {

    protected: // this could be private

        T m_t;

    public:
        Class()
            :
              m_t(T())
        {}

        Class(T t)
            :
              m_t(t)
        {}

        T& getT()
        {
            return m_t;
        }

        template<typename U, typename W>
        friend Class<W> f (Class<T>& C, const Class<T>& D)
        {
            C.m_t += D.m_t;
            Class<W> R;
            std::cout << R.m_t << std::endl; // I don't want this to be possible
            return R;
        }
    };
}

int main()
{
    N::Class<int> C(42), D(24);
    std::cout << N::f<int, char>(C, D).getT() << std::endl;
}

error: undefined reference to N::Class<char> N::f<int, char>(N::Class<int>&, N::Class<int> const&)'.

At this point, I went on to try different compiler versions and discovered that the above works as it does without a namespace with GCC < 6 but not with GCC > 6. ICC 17 also seems to pick up on the protected member access inside f, but not ICC 18. Clang never picks it up.

Which is the intended behaviour? Should the definition be made available to the linker in this case without using N::f;?.


Edit 1:

To clarify, I want to retain the intended behaviour, namely restricting f to be a friend only to Class instantiations that match its arguments (so in the example f would be friend to Class<T> but not to Class<W>).

Fright answered 30/7, 2018 at 2:16 Comment(2)
The fact this works without namespace looks like a bug in the compilers. It seems to be related to ADL which is disabled with ::f<types>() or N::f<types>(). This is wrong since in the forward declaration the parameters of f() depend on U, the first template parameter, while in the definition the parameters do not depend on the template parmaeters.Lassie
The arguments of f in the definition depend on the template parameter T. The reason for having <typename U, typename W> before f is to match the forward declaration. In the accepted answer in the original question, the pattern is essentially the same: a struct template template<typename T> struct fs which contains a function f with arguments depending on T. Essentially, I am using Class itself as the helper struct, and that is why f needs to be defined inside the class. I am unaware of anything in the standard that forbids that, but maybe I'm missing something.Fright

© 2022 - 2024 — McMap. All rights reserved.