Search code examples
c++templatesdefault-parametersfriend-functionnon-member-functions

Error for default parameter value of non-member template friend function


Why does the following code compile with GCC but not with Clang? Who is right and why?

class TF
{
        private:
                struct S
                {
                };

        template <typename T> friend void F(T x, S s, int v = 5);
};

template <typename T>
void F(T x, TF::S s, int v)
{
}

I get following error with clang++:

    error: friend declaration specifying a default argument must be a definition
        template <typename T> friend void F(T x, S s, int v = 5);
                                          ^
    error: friend declaration specifying a default argument must be the only declaration
    void F(T x, TF::S s, int v)
         ^
    note: previous declaration is here
        template <typename T> friend void F(T x, S s, int v = 5);

GCC version: g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0

Clang version: clang version 6.0.0-1ubuntu2

How do I resolve this?


Solution

  • How do I resolve this?

    As the error message says, you must define the function template to be able to have default arguments:

        template <typename T>
        friend void F(T x, S s, int v = 5) {
            // ...
        }
    

    If you don't want that, you could remove the default argument and add an overload that acts as a proxy:

        template <typename T>
        friend void F(T x, S s, int v);
    
    // ...
    
    template <typename T>
    void F(T x, TF::S s, int v) {
        // ...
    }
    
    template <typename T>
    void F(T x, TF::S s) { F(x, s, 5); }
    

    Another option is to make a forward declaration of the function:

    template <typename T, typename S>
    void F(T x, S s, int v = 5);
    
    class TF {
    private:
        struct S {};
    
        template <typename T, typename S>
        friend void F(T x, S s, int v);
    };
    
    template <typename T, typename S>
    void F(T x, S s, int v) {
        static_assert(std::is_same_v<S, TF::S>);
        // ...
    }