lundi 30 juillet 2018

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

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>).

Aucun commentaire:

Enregistrer un commentaire