Befriending specific specialization works in gcc and msvc but not in clang

121 Views Asked by At

I want to make a single specialization a friend as shown below. I tried the following with C++20 but the program gets rejected by clang and is accepted by GCC and MSVC.

template <class T> 
struct Ext {
   
   struct Inner
   { 
       int m{};
   };
   /*Is there a way to make this specialization work with all the compilers?
     Note that I know that I can write template<typename S> friend void fun2(Ext<T>::Inner&)
     but I only want to make this specialization a friend
   */
   friend void fun2<T>(typename Ext<T>::Inner&); //C++20: works in gcc and msvc but rejected by clang   
};

template <class T>
void fun2(typename Ext<T>::Inner &p) 
{ 
     p.m = 10; 
}
int main()
{
    Ext<int>::Inner x; 
    fun2<int>(x); 
}

Demo

I have two questions:

  1. Is there a way to make that specialization void fun2<T>(typename Ext<T>::Inner&); a friend so that the program is accepted by all compilers? That is, I want a workaround for making this particular specialization a friend instead of all Ext.

  2. Is the program shown above(in my post) well-formed in C++20?


Clang says:

<source>:13:16: error: no candidate function template was found for dependent friend function template specialization
   13 |    friend void fun2<T>(typename Ext<T>::Inner&); //C++20: works in gcc and msvc but rejected by clang   
      |                ^
<source>:24:15: error: use of undeclared identifier 'x'
   24 |     fun2<int>(x); 
1

There are 1 best solutions below

0
user12002570 On

It seems the program is ill-formed and gcc and msvc are wrong in accepting the program.

First note that fun2<T> refers to a specialization of a function template. From temp.friend:

  1. A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class. For a friend function declaration that is not a template declaration:

    1.1) if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

    1.2)

    1.3)

    1.4)


Next, From temp.deduct.decl:

  1. In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations, explicit specializations, and certain friend declarations. This is also done to determine whether a deallocation function template specialization matches a placement operator new ([basic.stc.dynamic.deallocation], [expr.new]). In all these cases, P is the type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in [expr.new]. The deduction is done as described in [temp.deduct.type].

  2. If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered ([temp.func.order]), deduction fails and, in the declaration cases, the program is ill-formed.

(emphasis mine)

And in our example, the set is empty and so there is no match which implies that the program is ill-formed as per the above quoted reference.