Why does taking the address of an inline friend not result in code being produced. This works for regular inline functions. Is the declaration not matching the inline friend somehow?
#include <iostream>
namespace S
{
template <unsigned N>
class X
{
int i;
friend X operator (const X& a, const X& b) noexcept
{
(void)a;
(void)b;
return {};
}
};
inline X<256> operator (const X<256>&, const X<256>&) noexcept;
}
int main()
{
S::X<256> (*ptr)(const S::X<256>& a, const S::X<256>& b) noexcept = &::S::operator ;
std::cout << (void*)ptr << std::endl;
return 0;
}
$ g --version
g (GCC) 11.2.1 20220401 (Red Hat 11.2.1-10)
$ g -std=c 17 test2.cpp
test2.cpp:18:13: warning: inline function ‘S::X<N> S::operator (const S::X<N>&, const S::X<N>&) [with unsigned int N = 256]’ used but never defined
18 | inline X<N> operator (const X<N>&, const X<N>&) noexcept;
| ^~~~~~~~
/usr/bin/ld: /tmp/ccoA1Nxj.o: in function `main':
test2.cpp:(.text 0xc): undefined reference to `S::X<256u> S::operator <256u>(S::X<256u> const&, S::X<256u> const&)'
collect2: error: ld returned 1 exit status
CodePudding user response:
There is no argument-dependent lookup when taking the address of an overload set.
So, the initialization of the function pointer doesn't require the compiler to instantiate X<256>
to figure out whether there is a operator
in it. For an unqualified function call this would happen.
Neither does the declaration of operator
outside the class require any of the involved types to be complete. So there is no implicit instantiation of X<256>
there either.
Consequently, when the address of the function is taken, X<256>
has not been instantiated and so the compiler can't be aware of the friend
definition of the function, which is then also not instantiated.
In the end, there is no definition for the function, which however was ODR-used by taking its address and therefore the program is ill-formed, no diagnostic required.
This can be fixed by having X<256>
be instantiated before taking the address, e.g. by writing static_assert(sizeof(S::X<256>));
before main
to cause implicit instantiation or template S::X<256>;
to cause explicit instantiation.