2

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?

Meekaa Saangoo
  • 309
  • 1
  • 2
  • 8

2 Answers2

1

This is a gcc's bug which has been fixed; clang is correct. When specifying default argument in friend declaration,

If a friend declaration specifies a default, it must be a friend function definition, and no other declarations of this function are allowed in the translation unit.

That means you should define the function when declaring it as friend at the same time.

From the standard, [dcl.fct.default]/4

If a friend declaration specifies a default argument expression, that declaration shall be a definition and shall be the only declaration of the function or function template in the translation unit.

BTW: Gcc 11 doesn't compile either.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • > "If a friend declaration specifies a default argument expression, that declaration shall be a definition and shall be the only declaration of the function or function template in the translation unit."... The question is why is there such a rule? – Meekaa Saangoo Aug 07 '20 at 07:44
  • @MeekaaSaangoo Sorry I don't know either, why the committee decided to make such a rule in the standard. You're asking which compiler is corrent, I answered it based on the standard. You might want to ask a new language-lawyer question about why the rule exists. – songyuanyao Aug 07 '20 at 08:02
1

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>);
    // ...
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108