Search code examples
c++templatesfriend

problems with resolving friend function of template


I have some trouble getting this to work. Here is a MVCE of my problem that passes the compilation phase

template<typename T>
struct foo
{
    using type = T;
    friend type bar(foo const& x) { return x.X; }
    foo(type x) : X(x) {}
  private:
    type X;
};

template<typename> struct fun;
template<typename T> fun<T> bar(foo<T> const&, T);  // forward declaration

template<typename T>
struct fun
{
    using type = T;
    friend fun bar(foo<type> const& x, type y)
    { return {bar(x)+y}; }
  private:
    fun(type x) : X(x) {}
    type X;
};

int main()
{
    foo<int> x{42};
    fun<int> y = bar(x,7);    // called here
};

The forward declaration is required for the compiler to resolve the call in main() (see this answer for the reason). However, the compiler now complains in the linking/loading phase:

Undefined symbols for architecture x86_64: "fun bar(foo const&, int)", referenced from: _main in foo-00bf19.o ld: symbol(s) not found for architecture x86_64

even though the function is defined in the friend declaration. If instead, I move the definition outside of that of struct func<>, i.e.

template<typename T>
struct fun
{
    using type = T;
    friend fun bar(foo<type> const& x, type y);
  private:
    fun(type x) : X(x) {}
    type X;
};

template<typename T>
inline fun<T> bar(foo<T> const& x, T y)
{ return {bar(x)+y}; }

compilation fails with

foo.cc:29:10: error: calling a private constructor of class 'fun<int>'
{ return {bar(x)+y}; }

So, how can I get this to work? (compiler: Apple LLVM version 9.0.0 (clang-900.0.39.2), c++11)


Solution

  • Friend declaration inside of fun must match function template forward declaration, otherwise it will spawn an unrelated function:

    template<typename TT> fun<TT> friend ::bar(foo<TT> const &, TT);
    

    While definition should be placed outside:

    template<typename T> fun<T> bar(foo<T> const& x, T y)
    { return {bar(x)+y}; }
    

    online compiler

    The shorter code to demonstrate the problem would be:

    void foo(void);
    
    template<typename T>
    struct bar
    {
        friend void foo(void) {}
    };
    
    int main()
    {
        foo(); // undefined reference to `foo()'
        return 0;
    }
    

    In-class function definition will never be used:

    17.8.1 Implicit instantiation [temp.inst]

    1. The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends;